# coding: utf8

import numpy as np
import pandas as pd
import random


class Demo:

    def __init__(self):
        self.df1 = pd.DataFrame(
            data=[[(x + 1) ** y for y in range(3)] for x in range(3)],
            columns=list('ABC'),
            index=pd.date_range('2010.1.1', periods=3),
            dtype=np.int8)
        # 拷贝df1,不共享
        self.df2 = pd.DataFrame(data=self.df1, copy=True)
        self.df2.index = range(3)
        # 与df2共享数据
        self.df3 = pd.DataFrame(data=self.df2, copy=False)
        self.df_mindex1 = None
        self.df_mindex2 = None

    def index_index(self):
        """
        Index(data=None, dtype=None, copy=False, name=None, fastpath=False, tupleize_cols=True, **kwargs)

          Immutable ndarray implementing an ordered, sliceable set. The basic object
          storing axis labels for all pandas objects

          Parameters
          ----------
          data : array-like (1-dimensional)
          dtype : NumPy dtype (default: object)
              If dtype is None, we find the dtype that best fits the data.
              If an actual dtype is provided, we coerce to that dtype if it's safe.
              Otherwise, an error will be raised.
          copy : bool
              Make a copy of input ndarray
          name : object
              Name to be stored in the index
          tupleize_cols : bool (default: True)
              When True, attempt to create a MultiIndex if possible

          Notes
          -----
          An Index instance can **only** contain hashable objects

          Examples
          --------
        >>> pd.Index([1, 2, 3])
        Int64Index([1, 2, 3], dtype='int64')

        >>> pd.Index(list('abc'))
        Index(['a', 'b', 'c'], dtype='object')
        """
        print('='*80)
        print("one-dime index")
        print("index11 = pd.Index([1, 2, 3], dtype=np.int8)")
        index1 = pd.Index([1, 2, 3], dtype=np.int8)
        print(index1)
        print("index12 = pd.Index([1, 2, 3], dtype=np.int8)")
        index1 = pd.Index([1, 2, 3], dtype=np.int8)
        print(index1)

        print('='*80)
        print("multi-index")
        print("index21 = pd.Index([(1, 2), (1, 3), (2, 2), (3, 3)], tupleiz_cols=True)")
        index21 = pd.Index([(1, 2), (1, 3), (2, 2), (3, 3)], tupleiz_cols=True)
        print(index21)
        print("index21.nlevels = {}".format(index21.nlevels))

        print('-'*80)
        print("不使用元组，会变为单维索引")

        print("-."*40)
        print("元素为列表")
        print("index22 = pd.Index([[1, 2], (1, 3), (2, 2), (3, 3)], tupleiz_cols=True)")
        index22 = pd.Index([[1, 2], (1, 3), (2, 2), (3, 3)], tupleiz_cols=True)
        print(index22)
        print("index22.nlevels = {}".format(index22.nlevels))

        print("-."*40)
        print("使用数组")
        print("index23 = pd.Index(np.array([[1, 2], (1, 3), (2, 2), (3, 3)]), tupleiz_cols=True)")
        index23 = pd.Index(np.array([[1, 2], (1, 3), (2, 2), (3, 3)]), tupleiz_cols=True)
        print(index23)
        print("index23.nlevels = {}".format(index23.nlevels))

        print('-'*80)

    def index_timestamp(self):
        """
        Timestamp(ts_input=<object object at 0x000001F9F435F1D0>, freq=None, tz=None, unit=None,
                   year=None, month=None, day=None, hour=None, minute=None, second=None,
                   microsecond=None, nanosecond=None, tzinfo=None)

         Pandas replacement for datetime.datetime

         Timestamp is the pandas equivalent of python's Datetime
         and is interchangeable with it in most cases. It's the type used
         for the entries that make up a DatetimeIndex, and other timeseries
         oriented data structures in pandas.

         Parameters
         ----------
         ts_input : datetime-like, str, int, float
             Value to be converted to Timestamp
         freq : str, DateOffset
             Offset which Timestamp will have
         tz : str, pytz.timezone, dateutil.tz.tzfile or None
             Time zone for time which Timestamp will have.
         unit : str
             Unit used for conversion if ts_input is of type int or float. The
             valid values are 'D', 'h', 'm', 's', 'ms', 'us', and 'ns'. For
             example, 's' means seconds and 'ms' means milliseconds.
         year, month, day : int
             .. versionadded:: 0.19.0
         hour, minute, second, microsecond : int, optional, default 0
             .. versionadded:: 0.19.0
         nanosecond : int, optional, default 0
             .. versionadded:: 0.23.0
         tzinfo : datetime.tzinfo, optional, default None
             .. versionadded:: 0.19.0
        """
        """
        min-timestamp: Timestamp('1677-09-21 00:12:44')
        max-timestamp: Timestamp('2262-04-11 23:47:16')
        
        min-datetime-timestamp: datetime.datetime(1970, 1 ,2, 8)
        min-datetime-timestamp: datetime.datetime(3001, 1 ,18, 23, 59, 59)
        """
        import datetime

        print("参数freq：设定该时间戳的增长步长, 称为日期偏移量DateOffset")
        print("ts1 = pd.Timestamp(datetime.datetime(2010, 10, 1), freq='2H')")
        ts1 = pd.Timestamp(datetime.datetime(2010, 10, 1), freq='2H')
        for j in range(3):
            print("ts1 + {}*ts1.freq = {}".format(j+1, ts1+(j+1)*ts1.freq))

        print("ts2 = pd.Timestamp('2010-1-1', freq='2H', tz='UTC')")
        ts2 = pd.Timestamp('2010-1-1', freq='2H', tz='UTC')
        print(ts2)

        print("ts3 = pd.Timestamp('2010-1-1', freq='2d', tz='Asia/Shanghai')")
        ts3 = pd.Timestamp('2010-1-1', freq='2d', tz='Asia/Shanghai')
        print(ts3)
        for j in range(3):
            print("ts3 + {}*ts3.freq = {}".format(j+1, ts3+(j+1)*ts3.freq))

    def index_datetimeindex(self):
        """
        --- version 0.24.0 ---
        DatetimeIndex(data=None, freq=None, start=None, end=None, periods=None, tz=None, normalize=False,
                      closed=None, ambiguous='raise', dayfirst=False, yearfirst=False,
                      dtype=None, copy=False, name=None, verify_integrity=True)

        Immutable ndarray of datetime64 data, represented internally as int64, and
        which can be boxed to Timestamp objects that are subclasses of datetime and
        carry metadata such as frequency information.

        Parameters
        ----------
        data  : array-like (1-dimensional), optional
            Optional datetime-like data to construct index with
        copy  : bool
            Make a copy of input ndarray
        freq : string or pandas offset object, optional
            One of pandas date offset strings or corresponding objects
        start : starting value, datetime-like, optional
            If data is None, start is used as the start point in generating regular
            timestamp data.
        periods  : int, optional, > 0
            Number of periods to generate, if generating index. Takes precedence
            over end argument
        end   : end time, datetime-like, optional
            If periods is none, generated index will extend to first conforming
            time on or just past end argument
        closed : string or None, default None
            Make the interval closed with respect to the given frequency to
            the 'left', 'right', or both sides (None)
        tz : pytz.timezone.common_timezones or dateutil.tz.tzfile
        ambiguous : 'infer', bool-ndarray, 'NaT', default 'raise'
            - 'infer' will attempt to infer fall dst-transition hours based on
              order
            - bool-ndarray where True signifies a DST time, False signifies a
              non-DST time (note that this flag is only applicable for ambiguous
              times)
            - 'NaT' will return NaT where there are ambiguous times
            - 'raise' will raise an AmbiguousTimeError if there are ambiguous times
        name : object
            Name to be stored in the index
        dayfirst : bool, default False
            If True, parse dates in `data` with the day first order
        yearfirst : bool, default False
            If True parse dates in `data` with the year first order
        """

        """
        --- version 1.2.1
         |  DatetimeIndex(data=None, freq=<object object at 0x000001D43852E0D0>, tz=None, 
                          normalize=False, closed=None, ambiguous='raise', dayfirst=False, 
                          yearfirst=False, dtype=None, copy=False, name=None)
        
         |  Immutable ndarray-like of datetime64 data.
        
         |  Represented internally as int64, and which can be boxed to Timestamp objects
         |  that are subclasses of datetime and carry metadata.
        
         |  Parameters
         |  ----------
         |  data : array-like (1-dimensional), optional
         |      Optional datetime-like data to construct index with.
         |  freq : str or pandas offset object, optional
         |      One of pandas date offset strings or corresponding objects. The string
         |      'infer' can be passed in order to set the frequency of the index as the
         |      inferred frequency upon creation.
         |  tz : pytz.timezone or dateutil.tz.tzfile or datetime.tzinfo or str
         |      Set the Timezone of the data.
         |  normalize : bool, default False
         |      Normalize start/end dates to midnight before generating date range.
         |  closed : {'left', 'right'}, optional
         |      Set whether to include `start` and `end` that are on the
         |      boundary. The default includes boundary points on either end.
         |  ambiguous : 'infer', bool-ndarray, 'NaT', default 'raise'
         |      When clocks moved backward due to DST, ambiguous times may arise.
         |      For example in Central European Time (UTC+01), when going from 03:00
         |      DST to 02:00 non-DST, 02:30:00 local time occurs both at 00:30:00 UTC
         |      and at 01:30:00 UTC. In such a situation, the `ambiguous` parameter
         |      dictates how ambiguous times should be handled.
        
         |      - 'infer' will attempt to infer fall dst-transition hours based on
         |        order
         |      - bool-ndarray where True signifies a DST time, False signifies a
         |        non-DST time (note that this flag is only applicable for ambiguous
         |        times)
         |      - 'NaT' will return NaT where there are ambiguous times
         |      - 'raise' will raise an AmbiguousTimeError if there are ambiguous times.
         |  dayfirst : bool, default False
         |      If True, parse dates in `data` with the day first order.
         |  yearfirst : bool, default False
         |      If True parse dates in `data` with the year first order.
         |  dtype : numpy.dtype or DatetimeTZDtype or str, default None
         |      Note that the only NumPy dtype allowed is ‘datetime64[ns]’.
         |  copy : bool, default False
         |      Make a copy of input ndarray.
         |  name : label, default None
         |      Name to be stored in the index.
        """
        import datetime

        # deprecated augments in new version: start, end, periods
        if False and pd.__version__ < '1.0.0':
            print('-'*80)
            print("data is first to used: data is used, start not used")
            print("pd.DatetimeIndex(data=[datetime.datetime(2010, 1, j+1) for j in range(5)],\n\
                             start=datetime.datetime(2009, 1, 1),\n\
                             periods=3,\n\
                             tz='America/New_York'\n\
                             )")
            print(pd.DatetimeIndex(data=[datetime.datetime(2010, 1, j+1) for j in range(5)],
                                   start=datetime.datetime(2009, 1, 1),
                                   periods=3,
                                   tz='America/New_York',
                                   dayfirst=True
                                   ))

            print('-'*80)
            print("start, end ,periods, freq can not be used together")
            print('-.'*40)
            print("start, end, periods: ")
            print(pd.DatetimeIndex(start=datetime.datetime(2010, 1, 1),
                                   end=datetime.datetime(2010, 2, 1),
                                   periods=3))
            print('-.'*40)
            print("start, end, periods: the last time of index-sequence can not exceed end-point")
            print("--- closed='right'")
            print("pd.DatetimeIndex(start=datetime.datetime(2010, 1, 1),\n\
                    end=datetime.datetime(2010, 3, 1),\n\
                    freq='20D',\n\
                    closed='right')")
            print(pd.DatetimeIndex(start=datetime.datetime(2010, 1, 1),
                                   end=datetime.datetime(2010, 3, 1),
                                   freq='20D',
                                   closed='right'))
            print("--- closed='left'")
            print("pd.DatetimeIndex(start=datetime.datetime(2010, 1, 1),\n\
                    end=datetime.datetime(2010, 3, 1),\n\
                    freq='20D',\n\
                    closed='left')")
            print(pd.DatetimeIndex(start=datetime.datetime(2010, 1, 1),
                                   end=datetime.datetime(2010, 3, 1),
                                   freq='20D',
                                   closed='left'))

            print("--- closed=None")
            print("pd.DatetimeIndex(start=datetime.datetime(2010, 1, 1),\n\
                    end=datetime.datetime(2010, 3, 1),\n\
                    freq='20D',\n\
                    closed=None)")
            print(pd.DatetimeIndex(start=datetime.datetime(2010, 1, 1),
                                   end=datetime.datetime(2010, 3, 1),
                                   freq='20D',
                                   closed=None))

            print('-.'*40)
            print("start, freq, periods: create the index-sequence: length = periods, step = freq, start at start")
            print("pd.DatetimeIndex(start=datetime.datetime(2010, 1, 1), freq='20H', periods=3)")
            print(pd.DatetimeIndex(start=datetime.datetime(2010, 1, 1), freq='20H', periods=3))
            print('-.'*40)
            print("end, freq, periods: create the index-sequence: length = periods, step = freq, end at end")
            print("pd.DatetimeIndex(end=datetime.datetime(2010, 1, 1), freq='20H', periods=3)")
            print(pd.DatetimeIndex(end=datetime.datetime(2010, 1, 1), freq='20H', periods=3))

        print("="*80)
        print("freq setting ")
        print('-.'*40)
        print("pd.DatetimeIndex(['11/06/2011 00:00', '11/06/2011 01:00', \n"
              "                  '11/06/2011 01:00', '11/06/2011 02:00'])")
        print(pd.DatetimeIndex(['11/06/2011 00:00', '11/06/2011 01:00',
                                '11/06/2011 02:00', '11/06/2011 03:00'],
                               ))
        print('-.'*40)
        print("# use DateOffset, Timedelta as freq")
        print("pd.DatetimeIndex(['11/06/2011 00:00:00', '11/06/2011 00:00:03'],\n\
                               freq=pd.DateOffset(3))")
        print(pd.DatetimeIndex(['11/06/2011 00:00:00', '11/09/2011 00:00:00'],
                               freq=pd.DateOffset(3)))
        print("pd.DatetimeIndex(['11/06/2011 00:00:00', '11/06/2011 03:00:00'],\n\
                               freq=pd.tseries.offsets.Hour(3))")
        print(pd.DatetimeIndex(['11/06/2011 00:00:00', '11/06/2011 03:00:00'],
                               freq=pd.tseries.offsets.Hour(3)))
        print("pd.DatetimeIndex(['11/06/2011 00:00:00', '11/06/2011 00:00:03'],\n\
                               freq=pd.Timedelta(3*10**9))")
        print(pd.DatetimeIndex(['11/06/2011 00:00:00', '11/06/2011 00:00:03'],
                               freq=pd.Timedelta(3*10**9)))

        print("-"*80)
        print("fail: ambiguous used to infer with None")
        print("pd.DatetimeIndex(['11/06/2011 00:00', '11/06/2011 01:00', \n"
              "                  '11/06/2011 01:00', '11/06/2011 02:00']))")
        print(pd.DatetimeIndex(['11/06/2011 00:00', '11/06/2011 01:00',
                                '11/06/2011 01:00', '11/06/2011 02:00']))

        print("-"*80)
        print("fail: ambiguous used to set NaT with 'NaT'")
        print("pd.DatetimeIndex(['11/06/2011 00:00', '11/06/2011 01:00', \n"
              "                  None, '11/06/2011 02:00']),\n"
              "                 ambiguous='NaT')")
        print(pd.DatetimeIndex(['11/06/2011 01:00', '11/06/2011 01:00', '11/06/11', '11/06/2011 03:00'],
                               ambiguous=[False, False, True, True]
                               ))

    def index_date_range(self):
        """
        date_range(start=None, end=None, periods=None, freq=None, tz=None, normalize=False,
                   name=None, closed=None, **kwargs)
            Return a fixed frequency DatetimeIndex.

            Parameters
            ----------
            start : str or datetime-like, optional
                Left bound for generating dates.
            end : str or datetime-like, optional
                Right bound for generating dates.
            periods : integer, optional
                Number of periods to generate.
            freq : str or DateOffset, default 'D' (calendar daily)
                Frequency strings can have multiples, e.g. '5H'. See
                :obj:`here <timeseries.offset_aliases>` for a list of
                frequency aliases.
            tz : str or tzinfo, optional
                Time zone name for returning localized DatetimeIndex, for example
                'Asia/Hong_Kong'. By default, the resulting DatetimeIndex is
                timezone-naive.
            normalize : bool, default False
                Normalize start/end dates to midnight before generating date range.
            name : str, default None
                Name of the resulting DatetimeIndex.
            closed : {None, 'left', 'right'}, optional
                Make the interval closed with respect to the given frequency to
                the 'left', 'right', or both sides (None, the default).
            **kwargs
                For compatibility. Has no effect on the result.

            Returns
            -------
            rng : DatetimeIndex

        """
        dr = pd.date_range(start='2010-1-2 10:20:30', periods=3, freq='10s', tz='Asia/Shanghai')
        print(
            f"{'-'*80}\n"
            " >>> pd.date_range(start='2010-1-1', periods=3, freq='1D2h30min')\n"
            f"{pd.date_range(start='2010-1-1', periods=3, freq='1D2h30min')}\n"

            f"{'-'*80}\n"
            " >>> pd.date_range(start='2010-1-1', end='2010-1-2', periods=3)\n"
            f"{pd.date_range(start='2010-1-1', end='2010-1-2', periods=3)}\n"

            f"{'-'*80}\n"
            " >>> dr = pd.date_range(start='2010-1-2 10:20:30', periods=3, freq='A-jan')\n"
            " >>> dr"
            f"{pd.date_range(start='2010-1-2 10:20:30', periods=3, freq='A-jan')}\n"

            f"{'-'*80}\n"
            " >>> pd.date_range(end='2010-1-2 10:20:30', periods=3, freq='10s', closed='left')\n"
            " --- # left is closed, right is open\n"
            f"{pd.date_range(end='2010-1-2 10:20:30', periods=3, freq='10s', closed='left')}\n"

            f"{'-'*80}\n"
            " >>> pd.date_range(start='2010-1-2 10:20:30', periods=3, freq='10s', normalize=True)\n"
            f"{pd.date_range(start='2010-1-2 10:20:30', periods=3, freq='10s', normalize=True)}\n"

            f"{'-'*80}\n"
            " >>> dr = pd.date_range(start='2010-1-2 10:20:30', periods=3, freq='10s', tz='Asia/Shanghai')\n"
            f"{pd.date_range(start='2010-1-2 10:20:30', periods=3, freq='10s', tz='Asia/Shanghai')}\n"
            f"{dr}\n"

            f"{'-.'*40}"
            " >>> dr.tz_convert('UTC')\n"
            f"{dr.tz_convert('UTC')}\n"
            )

    def date_dateoffset(self):
        """
        class DateOffset(RelativeDeltaOffset)
         |  Standard kind of date increment used for a date range.
         |
         |  Works exactly like relativedelta in terms of the keyword args you
         |  pass in, use of the keyword n is discouraged-- you would be better
         |  off specifying n in the keywords you use, but regardless it is
         |  there for you. n is needed for DateOffset subclasses.
         |
         |  DateOffset work as follows.  Each offset specify a set of dates
         |  that conform to the DateOffset.  For example, Bday defines this
         |  set to be the set of dates that are weekdays (M-F).  To syntax if a
         |  date is in the set of a DateOffset dateOffset we can use the
         |  is_on_offset method: dateOffset.is_on_offset(date).
         |
         |  If a date is not on a valid date, the rollback and rollforward
         |  methods can be used to roll the date to the nearest valid date
         |  before/after the date.
         |
         |  DateOffsets can be created to move dates forward a given number of
         |  valid dates.  For example, Bday(2) can be added to a date to move
         |  it two business days forward.  If the date does not start on a
         |  valid date, first it is moved to a valid date.  Thus pseudo code
         |  is:
         |
         |  def __add__(date):
         |    date = rollback(date) # does nothing if date is valid
         |    return date + <n number of periods>
         |
         |  When a date offset is created for a negative number of periods,
         |  the date is first rolled forward.  The pseudo code is:
         |
         |  def __add__(date):
         |    date = rollforward(date) # does nothing is date is valid
         |    return date + <n number of periods>
         |
         |  Zero presents a problem.  Should it roll forward or back?  We
         |  arbitrarily have it rollforward:
         |
         |  date + BDay(0) == BDay.rollforward(date)
         |
         |  Since 0 is a bit weird, we suggest avoiding its use.
         |
         |  Parameters
         |  ----------
         |  n : int, default 1
         |      The number of time periods the offset represents.
         |  normalize : bool, default False
         |      Whether to round the result of a DateOffset addition down to the
         |      previous midnight.
         |  **kwds
         |      Temporal parameter that add to or replace the offset value.
         |
         |      Parameters that **add** to the offset (like Timedelta):
         |
         |      - years
         |      - months
         |      - weeks
         |      - days
         |      - hours
         |      - minutes
         |      - seconds
         |      - microseconds
         |      - nanoseconds
         |
         |      Parameters that **replace** the offset value:
         |
         |      - year
         |      - month
         |      - day
         |      - weekday
         |      - hour
         |      - minute
         |      - second
         |      - microsecond
         |      - nanosecond.
         |
         |  See Also
         |  --------
         |  dateutil.relativedelta.relativedelta : The relativedelta type is designed
         |      to be applied to an existing datetime an can replace specific components of
         |      that datetime, or represents an interval of time.
         |
         |  Examples
         |  --------
         |  >>> from pandas.tseries.offsets import DateOffset
         |  >>> ts = pd.Timestamp('2017-01-01 09:10:11')
         |  >>> ts + DateOffset(months=3)
         |  Timestamp('2017-04-01 09:10:11')
         |
         |  >>> ts = pd.Timestamp('2017-01-01 09:10:11')
         |  >>> ts + DateOffset(months=2)
         |  Timestamp('2017-03-01 09:10:11')
         |
         |  Method resolution order:
         |      DateOffset
         |      RelativeDeltaOffset
         |      BaseOffset
         |      builtins.object
         |
         |  Methods defined here:
         |
         |  __setattr__(self, name, value)
         |
         |  ----------------------------------------------------------------------
         |  Data descriptors defined here:
         |
         |  __dict__
         |      dictionary for instance variables (if defined)
         |
         |  __weakref__
         |      list of weak references to the object (if defined)
         |
         |  ----------------------------------------------------------------------
        """

    def index_periodindex(self):
        """
        --- version 1.2.1
        class PeriodIndex(pandas.core.indexes.datetimelike.DatetimeIndexOpsMixin)
         |  PeriodIndex(data=None, ordinal=None, freq=None, tz=None, dtype=None, copy=False, name=None, **fields)
         |
         |  Immutable ndarray holding ordinal values indicating regular periods in time.
         |
         |  Index keys are boxed to Period objects which carries the metadata (eg,
         |  frequency information).
         |
         |  Parameters
         |  ----------
         |  data : array-like (1d int np.ndarray or PeriodArray), optional
         |         Optional period-like data to construct index with.
         |  freq : str or period object, optional
         |         One of pandas period strings or corresponding objects.
         |  year : int, array, or Series, default None
         |  month : int, array, or Series, default None
         |  quarter : int, array, or Series, default None
         |  day : int, array, or Series, default None
         |  hour : int, array, or Series, default None
         |  minute : int, array, or Series, default None
         |  second : int, array, or Series, default None
         |  tz : object, default None
         |       Timezone for converting datetime64 data to Periods.
         |  dtype : str or PeriodDtype, default None
         |  copy : bool
         |      Make a copy of input ndarray.
         |
         |  Attributes
         |  ----------
         |  day
         |  dayofweek
         |  day_of_week
         |  dayofyear
         |  day_of_year
         |  days_in_month
         |  daysinmonth
         |  end_time
         |  freq
         |  freqstr
         |  hour
         |  is_leap_year
         |  minute
         |  month
         |  quarter
         |  qyear
         |  second
         |  start_time
         |  week
         |  weekday
         |  weekofyear
         |  year
         |
         |  Methods
         |  -------
         |  asfreq
         |  strftime
         |  to_timestamp

        """
        """
         |  Period(value=None, freq=None, ordinal=None, 
                   year=None, month=None, quarter=None, day=None, 
                   hour=None, minute=None, second=None)
         
            表示一个时间段，也称为时期。由某个时间点表示的一个时间区间，区间的长度由freq决定。
         |  Represents a period of time

         |  Parameters
         |  ----------
         |  value : Period or compat.string_types, default None
         |          The time period represented (e.g., '4Q2005')
                    输入值，Period对象，或compat.string_types，缺省为None

         |  freq : str, default None
         |         One of pandas period strings or corresponding objects
                   类同DatetimeIndex的参数freq， Pandas周期字符串或相关对象，如'10h'，表示10个小时。
                   注意表示时间区间时，小时分等在单位限定范围内，如h的表示在0-23.

            以下参数使用时间单位为量纲，设置输入值
         |  year : int, default None
         |  month : int, default 1
         |  quarter : int, default None
         |  day : int, default 1
         |  hour : int, default 0
         |  minute : int, default 0
         |  second : int, default 0
            
            *参数ordinal未见解释
        """
        print('='*80)
        print("--- Period")
        print("pd.Period('2010-1-1 10:10', freq='10h')")
        print(pd.Period('2010-1-1 10:10', freq='10h'))

        print('='*80)
        print("--- PeriodIndex")
        print("--- data+freq")
        print('-.'*40)
        print("set freq as year-month: A-XXX/Y-XXX")
        print("pd.PeriodIndex(data=['2000-3', '2002-6'], freq='A-sep')")
        print(pd.PeriodIndex(data=['2000-3', '2002-6'], freq='A-sep'))
        print('-.'*40)
        print("set freq as quarter-month: Q-XXX")
        print("pd.PeriodIndex(data=[2000, 2002], freq='Q-Feb')")
        print(pd.PeriodIndex(data=[2000, 2002], freq='Q-Feb'))
        print('-.'*40)
        print("set freq as hour: xxh/xxH")
        print("pd.PeriodIndex(data=['2000-3-1 01:00:00', '2000-3-1 02:00:00'], freq='h')")
        print(pd.PeriodIndex(data=['2000-3-1 01:03:00', '2000-3-1 02:20:03'], freq='h'))
        print('-.'*40)
        print("use Period value in data")
        print("pd.Period('2000-3-1 01:03:00', freq='h')")
        print("pd.PeriodIndex(data=[pdo+j for j in range(3)], freq='h')")
        print("*** freq must match the data's freq, otherwise exception found: \n"
              "    pandas._libs.tslibs.period.IncompatibleFrequency")
        pdo = pd.Period('2000-3-1 01:03:00', freq='h')
        print(pd.PeriodIndex(data=[pdo+j for j in range(3)], freq='h'))

        print('-'*80)
        print("set data: year, month: 1-12, freq: A-XXX/Y-XXX")
        print("pd.PeriodIndex(year=[2000, 2002], month=[1, 9], freq='A-May')")
        print(pd.PeriodIndex(year=[2000, 2002], month=[1, 9], freq='A-may'))

        print('-'*80)
        print("--- data = year+month+quarter")
        print("pd.PeriodIndex(year=[2000, 2002], month=[1, 11], quarter=[1, 3])")
        print(pd.PeriodIndex(year=[2000, 2002], month=[1, 11], quarter=[1, 3]))

        print('-'*80)
        print("--- data+freq")
        print(pd.PeriodIndex(data=['2010-01-01', '2010-02-5', '2010-04-02'], freq='M'))

        print('-'*80)
        print("--- Quarter setting")
        print(pd.PeriodIndex(data=['2010-01-01', '2010-02-5', '2010-04-02'], freq='Q-Jan'))
        print(pd.PeriodIndex(data=['2010-01-01', '2010-02-5', '2010-04-02'], freq='Q-Jan'))

    def index_period_range(self):
        """
        period_range(start=None, end=None, periods=None, freq=None, name=None)
            -> pandas.core.indexes.period.PeriodIndex

            Return a fixed frequency PeriodIndex.

            The day (calendar) is the default frequency.

            Parameters
            ----------
            start : str or period-like, default None
                Left bound for generating periods.
            end : str or period-like, default None
                Right bound for generating periods.
            periods : int, default None
                Number of periods to generate.
            freq : str or DateOffset, optional
                Frequency alias. By default the freq is taken from `start` or `end`
                if those are Period objects. Otherwise, the default is ``"D"`` for
                daily frequency.
            name : str, default None
                Name of the resulting PeriodIndex.

        """
        print('-'*80)
        print("set satrt, end, freq ")
        print("pd.period_range(start='2010-1-1', end='2010-5-1', freq='m')")
        print(pd.period_range(start='2010-1-1', end='2010-5-1', freq='m'))

        print("-"*80)
        print("set satrt, period, freq ")
        print("pd.period_range(start='2010-1-1', periods=3, freq='q')")
        print(pd.period_range(start='2010-1-1', periods=3, freq='q'))

        print("-"*80)
        print("set end, period, freq ")
        print("pd.period_range(end='2010-6-1', periods=3, freq='m')")
        print(pd.period_range(end='2010-6-1', periods=3, freq='m'))

        print("-"*80)
        print("set start, end, period fail, exact 2 of these 3 can be specified!")
        print("set start, end only")
        print("pd.period_range(start='2010-1-1', end='2010-2-1')")
        print(pd.period_range(start='2010-1-1', end='2010-1-3'))

    def index_timedeltaindex(self):
        """
        -- version 0.24.0
        class TimedeltaIndex(pandas.core.indexes.datetimelike.DatetimeIndexOpsMixin, pandas.core.indexes.
                             datetimelike.TimelikeOps, pandas.core.indexes.numeric.Int64Index)

        TimedeltaIndex(data=None, unit=None, freq=None, start=None, end=None, periods=None, closed=None,
                           dtype=None, copy=False, name=None, verify_integrity=True)
         |
        Immutable ndarray of timedelta64 data, represented internally as int64, and
        which can be boxed to timedelta objects
         |
        Parameters
        ----------
        data  : array-like (1-dimensional), optional
            Optional timedelta-like data to construct index with
        unit: unit of the arg (D,h,m,s,ms,us,ns) denote the unit, optional
            which is an integer/float number
        freq: a frequency for the index, optional
        copy  : bool
            Make a copy of input ndarray
        start : starting value, timedelta-like, optional
            If data is None, start is used as the start point in generating regular
            timedelta data.
        periods  : int, optional, > 0
            Number of periods to generate, if generating index. Takes precedence
            over end argument
        end   : end time, timedelta-like, optional
            If periods is none, generated index will extend to first conforming
            time on or just past end argument

        Attributes
        ----------
        days
        seconds
        microseconds
        nanoseconds
        components
        inferred_freq

        Methods
        -------
        to_pytimedelta
        to_series
        round
        floor
        ceil
        to_frame
        """
        """
        -- version 1.2.1
        class TimedeltaIndex(pandas.core.indexes.datetimelike.DatetimeTimedeltaMixin)
         |  TimedeltaIndex(data=None, unit=None, freq=<object object at 0x000001D43852E0D0>, 
                           closed=None, dtype=dtype('<m8[ns]'), copy=False, name=None)
         |
         |  Immutable ndarray of timedelta64 data, represented internally as int64, and
         |  which can be boxed to timedelta objects.
         |
         |  Parameters
         |  ----------
         |  data  : array-like (1-dimensional), optional
         |          Optional timedelta-like data to construct index with.
         |  unit : unit of the arg (D,h,m,s,ms,us,ns) denote the unit, optional
         |         Which is an integer/float number.
         |  freq : str or pandas offset object, optional
         |         One of pandas date offset strings or corresponding objects. The string
         |         'infer' can be passed in order to set the frequency of the index as the
         |         inferred frequency upon creation.
         |  copy : bool
         |         Make a copy of input ndarray.
         |  name : object
         |         Name to be stored in the index.
        """
        """
        class Timedelta(_Timedelta)
         |  Timedelta(value=<object object at 0x000001F9F435F1B0>, unit=None, **kwargs)

         |  表示日期或时间差，即一段时间量。
         |  Represents a duration, the difference between two dates or times.

         |  该类型为Python中的时间差datetime.timedelta的等同类型，几乎可以在任何情况下对等交换使用
         |  Timedelta is the pandas equivalent of python's ``datetime.timedelta``
         |  and is interchangeable with it in most cases.
         |
         |  Parameters
         |  ----------
         |  value : Timedelta, timedelta, np.timedelta64, string, or integer
                    接收值可以为 Timedelta本身对象，timedelta(Python-datetime), np.timedelta64, 字符串 或 整数
         |  unit : string, {'ns', 'us', 'ms', 's', 'm', 'h', 'D'}, optional
         |      Denote the unit of the input, if input is an integer. Default 'ns'.
                   单位，为字符串，范围：{'ns', 'us', 'ms', 's', 'm', 'h', 'D'}，可选参数，缺省为纳秒：'ns'.
         |  days, seconds, microseconds, milliseconds, minutes, hours, weeks : numeric, optional
         |      Values for construction in compat with datetime.timedelta.
         |      np ints and floats will be coereced to python ints and floats.
         |
                可以周（weeks）、天（year）、小时（hours）、分（minutes）、秒（seconds）、毫秒（milliseconds）等参数表示输入量
                不支持years、months等作为输入量

         |  Notes
         |  -----
         |  The ``.value`` attribute is always in ns. 使用属性value时，获得的数据都是以ns为单位
         
        """
        print('='*80)
        print("--- Timedelta")
        print(pd.Timedelta(100, unit='m'))
        print(pd.Timedelta(hours=1))

        print('='*80)
        print("--- TimedeltaIndex")
        print("pd.TimedeltaIndex([10000, 200000, 5000000])")
        print(pd.TimedeltaIndex([10000, 200000, 5000000]))

        print("-"*80)
        print("pd.TimedeltaIndex([1, 2, 5], unit='m')")
        print(pd.TimedeltaIndex([1, 2, 5], unit='m'))

        print("-"*80)
        print("pd.TimedeltaIndex([1, 2, 3], freq='ns')")
        print(pd.TimedeltaIndex([1, 2, 3], freq='ns'))

        print("-"*80)
        print("pd.TimedeltaIndex([j*10**6 for j in range(3)], freq='ms')")
        print(pd.TimedeltaIndex([j*10**6 for j in range(3)], freq='ms'))

        print("-"*80)
        print("pd.TimedeltaIndex([j*10**9 for j in range(3)], freq='s')")
        print(pd.TimedeltaIndex([j*10**9 for j in range(3)], freq='s'))

        # print("-"*80)
        # print("pd.TimedeltaIndex(start=10, end=1000, freq='100h', unit='h')")
        # print(pd.TimedeltaIndex(start=10, end=1000, freq='t', unit='h'))

        # print("-"*80)
        # print("pd.TimedeltaIndex(start=10, periods=3, freq='100h', unit='m')")
        # print(pd.TimedeltaIndex(start=10, periods=3, freq='t'))

        # print("-"*80)
        # print("pd.TimedeltaIndex(start=10, periods=3, freq='ns', unit='m')")
        # print(pd.TimedeltaIndex(start=10, periods=3, freq='ns', closed='right'))

        # print("-"*80)
        # print("pd.TimedeltaIndex(data=[1000, 2000, 3000], freq='1000s', verify_intigerity=True)")
        # print(pd.TimedeltaIndex(data=[1000, 2000, 3000], unit='s', freq='1000s', verify_integrity=True))
        #
        # print("-."*40)
        # print("pd.TimedeltaIndex(start=10, periods=3, freq='s', verify_intigerity=False)")
        # print(pd.TimedeltaIndex(data=[1000, 2001, 3000], unit='s', freq='s', verify_integrity=False))
        #
        # print("-."*40)
        # print("pd.TimedeltaIndex(start=10, periods=3, freq='s', verify_intigerity=True)")
        # try:
        #     print(pd.TimedeltaIndex(data=[1000, 2001, 3000], unit='s', freq='s', verify_integrity=True))
        # except ValueError as e:
        #     print(e)

    def index_timedelta_range(self):
        """
        -- version 1.2.1
        timedelta_range(start=None, end=None, periods=None, freq=None, name=None, closed=None)
            -> pandas.core.indexes.timedeltas.TimedeltaIndex

            Return a fixed frequency TimedeltaIndex, with day as the default
            frequency.

            Parameters
            ----------
            start : str or timedelta-like, default None
                Left bound for generating timedeltas.
            end : str or timedelta-like, default None
                Right bound for generating timedeltas.
            periods : int, default None
                Number of periods to generate.
            freq : str or DateOffset, default 'D'
                Frequency strings can have multiples, e.g. '5H'.
            name : str, default None
                Name of the resulting TimedeltaIndex.
            closed : str, default None
                Make the interval closed with respect to the given frequency to
                the 'left', 'right', or both sides (None).

        """
        """
        class Timedelta(_Timedelta)
         |  Timedelta(value=<object object at 0x000001D43852E3B0>, unit=None, **kwargs)
         |
         |  Represents a duration, the difference between two dates or times.
         |
         |  Timedelta is the pandas equivalent of python's ``datetime.timedelta``
         |  and is interchangeable with it in most cases.
         |
         |  Parameters
         |  ----------
         |  value : Timedelta, timedelta, np.timedelta64, str, or int
                    使用str时，以格式‘0 days 00：00：01’方式描述，
                    由于天以上单位具有不固定性，不使用在时间差中
                    使用int时，单位以unit确定，缺省为纳秒ns
         |  unit : str, default 'ns'
         |      Denote the unit of the input, if input is an integer.
         |
         |      Possible values:
         |
         |      * 'W', 'D', 'T', 'S', 'L', 'U', or 'N'
         |      * 'days' or 'day'
         |      * 'hours', 'hour', 'hr', or 'h'
         |      * 'minutes', 'minute', 'min', or 'm'
         |      * 'seconds', 'second', or 'sec', 's'
         |      * 'milliseconds', 'millisecond', 'millis', or 'milli', 'ms'
         |      * 'microseconds', 'microsecond', 'micros', or 'micro', 'us'
         |      * 'nanoseconds', 'nanosecond', 'nanos', 'nano', or 'ns'.
                毫秒、微妙可以使用缩写ms、us
         |
         |  **kwargs
         |      Available kwargs: {days, seconds, microseconds,
         |      milliseconds, minutes, hours, weeks}.
                可以直接使用days,...weeks作为参数直接给定输入数据, 但不能与value并用
                
         |      Values for construction in compat with datetime.timedelta.
                用于构建的数据应与datetime.timedelta兼容
         
         |      Numpy ints and floats will be coerced to python ints and floats.
         |      如果使用Numpy的int和float类型，会强制转换为Python的int和float
         
         |  Notes
         |  -----
         |  The ``.value`` attribute is always in ns.
         |
         |  If the precision is higher than nanoseconds, the precision of the duration is
         |  truncated to nanoseconds.
        """
        print(" use Timedelta, str to start, end")
        print("-."*40)
        print(" >>> pd.timedelta_range(start=pd.Timedelta(10, unit='m'),\n"
              "                   end=pd.Timedelta(20, unit='m'),\n"
              "                   periods=3)")
        print(pd.timedelta_range(start=pd.Timedelta(10, unit='m'),
                                 end=pd.Timedelta(20, unit='m'),
                                 periods=3))
        print("-."*40)
        print(" >>> pd.timedelta_range(start='0 days 00:00:01',\n"
              "                   end='0 days 00:00:10',\n"
              "                   periods=3)")
        print(pd.timedelta_range(start='0 days 00:00:01',
                                 end='0 days 00:00:10',
                                 periods=3))
        print("-."*40)
        print(" >>> pd.timedelta_range(start=pd.Timedelta(weeks=1, days=2, hours=3),\n"
              "                        end=pd.Timedelta(weeks=2, days=1, hours=3.5),\n"
              "                        periods=3)")
        print(pd.timedelta_range(start=pd.Timedelta(weeks=1, days=2, hours=3),
                                 end=pd.Timedelta(weeks=2, days=1, hours=3.5),
                                 periods=3))

        print("-"*80)
        print(" >>> pd.timedelta_range(start=10, end=20, periods=3)")
        print(pd.timedelta_range(start=10, end=20, periods=3))

        print("-"*80)
        print(" >>> pd.timedelta_range(start=10000, end=20000, freq='6us')")
        print(pd.timedelta_range(start=10000, end=20000, freq='6us'))

        print("-"*80)
        print(" >>> pd.timedelta_range(start=0, periods=3, freq='h')")
        print(pd.timedelta_range(start=0, periods=3, freq='h'))

        print("-"*80)
        print(" >>> pd.timedelta_range(start=0, periods=3, freq='d', closed='left')")
        print(pd.timedelta_range(start=0, periods=3, freq='d', closed='left'))
        print("-."*40)
        print(" >>> pd.timedelta_range(start=0, periods=3, freq='d', closed='right')")
        print(pd.timedelta_range(start=0, periods=3, freq='d', closed='right'))
        print("-."*40)
        print(" >>> pd.timedelta_range(start=0, periods=3, freq='d', closed=None)")
        print(pd.timedelta_range(start=0, periods=3, freq='d', closed=None))

    def index_categoricaldtype(self):
        """
        Python enum.Enum

        An enumeration is a set of symbolic names (members) bound to unique, constant values. Within an enumeration,
        the members can be compared by identity, and the enumeration itself can be iterated over.

        >>> from enum import Enum
        >>> class Color(Enum):
        ...     RED = 1
        ...     GREEN = 2
        ...     BLUE = 3
        >>> print(Color.BLUE)
        Color.BLUE
        >>> a = Color(2)
        >>> print(a)
        Color.GREEN
        >>> b = Color(Color.RED)
        >>> print(b)
        Color.RED
        >>> print(type(b))
        <enum 'Color'>
        >>> sr = pd.Series([Color(i) for i in range(1, 4)])
        >>> print(sr)
        0      Color.RED
        1    Color.GREEN
        2     Color.BLUE
        dtype: object

        -- version 0.24.0
        Pandas Category
        class Categorical(pandas.core.arrays.base.ExtensionArray, pandas.core.base.PandasObject)
         |  Categorical(values, categories=None, ordered=None, dtype=None, fastpath=False)
         |
         |  Represents a categorical variable in classic R / S-plus fashion
         |
         |  `Categoricals` can only take on only a limited, and usually fixed, number
         |  of possible values (`categories`). In contrast to statistical categorical
         |  variables, a `Categorical` might have an order, but numerical operations
         |  (additions, divisions, ...) are not possible.
         |
         |  All values of the `Categorical` are either in `categories` or `np.nan`.
         |  Assigning values outside of `categories` will raise a `ValueError`. Order
         |  is defined by the order of the `categories`, not lexical order of the
         |  values.
         |
         |  Parameters
         |  ----------
         |  values : list-like
         |      The values of the categorical. If categories are given, values not in
         |      categories will be replaced with NaN.
         |  categories : Index-like (unique), optional
         |      The unique categories for this categorical. If not given, the
         |      categories are assumed to be the unique values of values.
         |  ordered : boolean, (default False)
         |      Whether or not this categorical is treated as a ordered categorical.
         |      If not given, the resulting categorical will not be ordered.
         |  dtype : CategoricalDtype
         |      An instance of ``CategoricalDtype`` to use for this categorical
         |
         |      .. versionadded:: 0.21.0
         |
         |  Attributes
         |  ----------
         |  categories : Index
         |      The categories of this categorical
         |  codes : ndarray
         |      The codes (integer positions, which point to the categories) of this
         |      categorical, read only.
         |  ordered : boolean
         |      Whether or not this Categorical is ordered.
         |  dtype : CategoricalDtype
         |      The instance of ``CategoricalDtype`` storing the ``categories``
         |      and ``ordered``.
         |
         |      .. versionadded:: 0.21.0
         |
         |  Methods
         |  -------
         |  from_codes
         |  __array__

        # 有序数据创建
        >>> genders = pd.Categorical(['男', '女', '男'], ordered=True)
        >>> print(genders)
        ['男', '女', '男']
        Categories (2, object): ['女' < '男']

        >>> sr = pd.Series(['男', '女', '女'])         # 枚举数据的Series
        >>> sr = sr.astype('category')
        >>> print(sr)
        0    男
        1    女
        2    女
        dtype: category
        Categories (2, object): ['女', '男']
        # >>> sr.cat.categories
        """

        """
        -- version 1.2.1    # used as dtype in Series and DataFrame
        
        class CategoricalDtype(PandasExtensionDtype, pandas.core.dtypes.base.ExtensionDtype)
        
         |  CategoricalDtype(categories=None, ordered: Union[bool, NoneType] = False)
         |
         |  Type for categorical data with the categories and orderedness.
         |
         |  Parameters
         |  ----------
         |  categories : sequence, optional
         |      Must be unique, and must not contain any nulls.
         |      The categories are stored in an Index,
         |      and if an index is provided the dtype of that index will be used.
         |  ordered : bool or None, default False
         |      Whether or not this categorical is treated as a ordered categorical.
         |      None can be used to maintain the ordered value of existing categoricals when
         |      used in operations that combine categoricals, e.g. astype, and will resolve to
         |      False if there is no existing ordered to maintain.
         |
         |
         |  Attributes
         |  ----------
         |  categories
         |  ordered
         |
         |  Methods
         |  -------
         |  None
         |
         |  See Also
         |  --------
         |  Categorical : Represent a categorical variable in classic R / S-plus fashion.
         |
         |  Notes
         |  -----
         |  This class is useful for specifying the type of a ``Categorical``
         |  independent of the values. See :obj:`categorical.categoricaldtype`
         |  for more.
         |
         |  Examples
         |  --------
         |  >>> t = pd.CategoricalDtype(categories=['b', 'a'], ordered=True)
         |  >>> pd.Series(['a', 'b', 'a', 'c'], dtype=t)
         |  0      a
         |  1      b
         |  2      a
         |  3    NaN
         |  dtype: category
         |  Categories (2, object): ['b' < 'a']
         |
         |  An empty CategoricalDtype with a specific dtype can be created
         |  by providing an empty index. As follows,
         |
         |  >>> pd.CategoricalDtype(pd.DatetimeIndex([])).categories.dtype
         |  dtype('<M8[ns]')
         |
         |  Method resolution order:
         |      CategoricalDtype
         |      PandasExtensionDtype
         |      pandas.core.dtypes.base.ExtensionDtype
         |      builtins.object
        """

        """
        Categorical(values, categories=None, ordered=None, dtype=None, fastpath=False)
        
        Represent a categorical variable in classic R / S-plus fashion.
        
        `Categoricals` can only take on only a limited, and usually fixed, number
        of possible values (`categories`). In contrast to statistical categorical
        variables, a `Categorical` might have an order, but numerical operations
        (additions, divisions, ...) are not possible.
        
        All values of the `Categorical` are either in `categories` or `np.nan`.
        Assigning values outside of `categories` will raise a `ValueError`. Order
        is defined by the order of the `categories`, not lexical order of the
        values.
        
        Parameters
        ----------
        values : list-like
            The values of the categorical. If categories are given, values not in
            categories will be replaced with NaN.
        categories : Index-like (unique), optional
            The unique categories for this categorical. If not given, the
            categories are assumed to be the unique values of `values` (sorted, if
            possible, otherwise in the order in which they appear).
        ordered : bool, default False
            Whether or not this categorical is treated as a ordered categorical.
            If True, the resulting categorical will be ordered.
            An ordered categorical respects, when sorted, the order of its
            `categories` attribute (which in turn is the `categories` argument, if
            provided).
        dtype : CategoricalDtype
            An instance of ``CategoricalDtype`` to use for this categorical.
         |
         |  Attributes
         |  ----------
         |  categories : Index
         |      The categories of this categorical
         |  codes : ndarray
         |      The codes (integer positions, which point to the categories) of this
         |      categorical, read only.
         |  ordered : bool
         |      Whether or not this Categorical is ordered.
         |  dtype : CategoricalDtype
         |      The instance of ``CategoricalDtype`` storing the ``categories``
         |      and ``ordered``.
        """

        print("="*80)
        print("use Python Enum in Series")
        print(" >>> from enum import Enum\n"
              " >>> from random import choice\n"
              " >>> class Gender(Enum):\n"
              " ...     Male = 1\n"
              " ...     Female = 2\n"
              " >>> sr = pd.Series([Gender(1), Gender(2), Gender(1)])\n"
              " >>> sr")
        from enum import Enum
        from random import choice

        class Gender(Enum):
            Male = 1
            Female = 2

        sr = pd.Series([Gender(1), Gender(2), Gender(1)])
        print(sr)

        print("="*80)
        print("-"*80)
        print(" # 使用Pandas枚举元类型CategoricalDtype定义枚举类型")
        print(" >>> CatGender = pd.CategoricalDtype(['男', '女'], ordered=True)\n"
              " >>> CatWeekday = pd.CategoricalDtype(['Wednesday', 'Tuesday', 'Monday', 'Thursday',\n"
              "                           'Friday', 'Sunday', 'Saturday'],\n"
              "                           )")
        CatGender = pd.CategoricalDtype(['男', '女'], ordered=True)
        CatWeekday = pd.CategoricalDtype(['Wednesday', 'Tuesday', 'Monday', 'Friday', 'Sunday', 'Saturday', 'Thursday'])

        print(" # 未使用枚举类型的数据集")
        print(" >>> df = pd.DataFrame(\n"
              "             {'id': range(100),\n"
              "              'gender': [choice('男', '女') for _ in range(100)],\n"
              "              'workday': [choice(weekdays) for _ in range(100]}\n"
              "              )")
        df = pd.DataFrame(
                {'id': range(100),
                 'gender': [choice(['男', '女']) for _ in range(100)],
                 'workday': [choice(CatWeekday.categories) for _ in range(100)]},
                )
        print(" >>> df")
        print(df)
        print(" >>> df.info()")
        print(df.info())

        print("-"*80)
        print(" # 在数据集中使用枚举类型， 利用astype将数据列类型改变为枚举类型")
        print(" >>> df['gender'] = df['gender'].astype(CatGender)")
        df['gender'] = df['gender'].astype(CatGender)
        print(" >>> df['workday'] = df['workday'].astype(CatWeekday)")
        df['workday'] = df['workday'].astype(CatWeekday)
        print(" >>> df")
        print(df)
        print(" >>> df.info()")
        print(df.info())

        print("-."*40)
        print(" # 使用Categorical.from_codes生成枚举类型数据")
        print(" >>> df['school'] = pd.Categorical.from_codes(\n"
              "        codes=[choice([0, 1] for _ in range(100)], \n"
              "        categories=['Sili Street', 'Yellow River'])\n")
        df['school'] = pd.Categorical.from_codes(
            codes=[choice([0, 1]) for _ in range(100)],
            categories=['Sili Street', 'Yellow River'])
        print(" >>> df")
        print(df)
        print(" >>> df.sort_values(by='workday')")
        print(df.sort_values(by='workday'))

        print("-."*40)
        print(" >>> df['school'].cat.categories = ['School-101', 'School-102']")
        df['school'].cat.categories = ['School-101', 'School-102']
        print(df)

        print("-."*40)
        print(" >>> df['school'] = df['school'].cat.rename_categories(['S101', 'S102'])")
        df['school'] = df['school'].cat.rename_categories(['S101', 'S102'])
        print(df)

        print("-."*40)
        print(" >>> df['school'] = df['school'].cat.rename_categories({'S101': 'S-101', 'S102': 'S-102'})")
        df['school'] = df['school'].cat.rename_categories({'S101': 'S-101', 'S102': 'S-102'})
        print(df)

        print("="*80)
        print("use Categorical to create category variable with CatgoricalDtype data")
        print(" >>> cat_month = pd.Categorical(\n"
              "                     values=['January', 'May', 'March'],\n"
              "                     categories=['January', 'February', 'March', 'April',\n" 
              "                                 'May', 'June', 'July', 'August', 'September',\n"
              "                                 'October', 'November', 'December'],\n"
              "                     ordered=True)")
        month_list = ['January', 'February', 'March', 'April',
                      'May', 'June', 'July', 'August', 'September',
                      'October', 'November', 'December']
        cat_month = pd.Categorical(values=[choice(month_list) for _ in range(100)],
                                   categories=month_list,
                                   ordered=True)
        print(cat_month)
        print(" >>> df['month'] = cat_month")
        df['month'] = cat_month
        print(" >>> df")
        print(df)
        print(" >>> df.sort_values(by='month')")
        print(df.sort_values(by='month'))

        print("-."*40)
        class_list = ['一班', '二班', '三班', '四班', '五班']
        print(" >>> cat_class = pd.Categorical(\n"
              "                     values=[choice(class_list) for _ range(5)],\n"
              "                     categories=class_list\n"
              "                     ordered=True)\n"
              " >>> df2 = df.iloc[0:5]\n"
              " >>> df2.index = cat_class")
        cat_class = pd.Categorical(
                        values=[choice(class_list) for _ in range(5)],
                        categories=class_list,
                        ordered=True)
        df2 = df.iloc[0:5]
        df2.index = cat_class
        print(" >>> df2")
        print(df2)
        print(" >>> df2.sort_index()")
        print(df2.sort_index())

        """
        class CategoricalIndex(pandas.core.indexes.extension.NDArrayBackedExtensionIndex,
                               pandas.core.accessor.PandasDelegate)

        CategoricalIndex(data=None, categories=None, ordered=None, dtype=None, copy=False, name=None)

        Index based on an underlying :class:`Categorical`.
         |
        CategoricalIndex, like Categorical, can only take on a limited,
        and usually fixed, number of possible values (`categories`). Also,
        like Categorical, it might have an order, but numerical operations
        (additions, divisions, ...) are not possible.
         |
        Parameters
        ----------
        data : array-like (1-dimensional)
            The values of the categorical. If `categories` are given, values not in
            `categories` will be replaced with NaN.
        categories : index-like, optional
            The categories for the categorical. Items need to be unique.
            If the categories are not given here (and also not in `dtype`), they
            will be inferred from the `data`.
        ordered : bool, optional
            Whether or not this categorical is treated as an ordered
            categorical. If not given here or in `dtype`, the resulting
            categorical will be unordered.
        dtype : CategoricalDtype or "category", optional
            If :class:`CategoricalDtype`, cannot be used together with
            `categories` or `ordered`.
        copy : bool, default False
            Make a copy of input ndarray.
        name : object, optional
            Name to be stored in the index.
         |
         |  Attributes
         |  ----------
         |  codes
         |  categories
         |  ordered
         |
         |  Methods
         |  -------
         |  rename_categories
         |  reorder_categories
         |  add_categories
         |  remove_categories
         |  remove_unused_categories
          Examples
         |  --------
         |  >>> pd.CategoricalIndex(["a", "b", "c", "a", "b", "c"])
         |  CategoricalIndex(['a', 'b', 'c', 'a', 'b', 'c'],
         |                   categories=['a', 'b', 'c'], ordered=False, dtype='category')
         |
         |  ``CategoricalIndex`` can also be instantiated from a ``Categorical``:
         |
         |  >>> c = pd.Categorical(["a", "b", "c", "a", "b", "c"])
         |  >>> pd.CategoricalIndex(c)
         |  CategoricalIndex(['a', 'b', 'c', 'a', 'b', 'c'],
         |                   categories=['a', 'b', 'c'], ordered=False, dtype='category')
         |
         |  Ordered ``CategoricalIndex`` can have a min and max value.
         |
         |  >>> ci = pd.CategoricalIndex(
         |  ...     ["a", "b", "c", "a", "b", "c"], ordered=True, categories=["c", "b", "a"]
         |  ... )
         |  >>> ci
         |  CategoricalIndex(['a', 'b', 'c', 'a', 'b', 'c'],
         |                   categories=['c', 'b', 'a'], ordered=True, dtype='category')
        """

        print("="*80)
        print(" >>> cai_class = pd.CategoricalIndex(data=[choice(class_list) for _ in range(5)])")
        cai_class = pd.CategoricalIndex(data=[choice(class_list) for _ in range(5)])
        print(" >>> cai_class")
        print(cai_class)
        print(" >>> isinstance(cai_class, pd.Index)")
        print(isinstance(cai_class, pd.Index))
        df2.index = cai_class
        print(" >>> df2")
        print(df2)
        print(" >>> df2.reindex(cai_class)")
        print(df2.reindex(cai_class))

    def index_intervalindex(self):
        """
        class IntervalIndex(pandas._libs.interval.IntervalMixin, pandas.core.indexes.extension.ExtensionIndex)
         |  IntervalIndex(data, closed=None, dtype=None, copy: bool = False, name=None, verify_integrity: bool = True)
         |
         |  Immutable index of intervals that are closed on the same side.
         |
         |  .. versionadded:: 0.20.0
         |
         |  Parameters
         |  ----------
         |  data : array-like (1-dimensional)
         |      Array-like containing Interval objects from which to build the
         |      IntervalIndex.
         |  closed : {'left', 'right', 'both', 'neither'}, default 'right'
         |      Whether the intervals are closed on the left-side, right-side, both or
         |      neither.
         |  dtype : dtype or None, default None
         |      If None, dtype will be inferred.
         |
         |      .. versionadded:: 0.23.0
         |  copy : bool, default False
         |      Copy the input data.
         |  name : object, optional
         |       Name to be stored in the index.
         |  verify_integrity : bool, default True
         |      Verify that the IntervalIndex is valid.
         |
         |  Attributes
         |  ----------
         |  left
         |  right
         |  closed
         |  mid
         |  length
         |  is_empty
         |  is_non_overlapping_monotonic
         |  is_overlapping
         |  values
         |
         |  Methods
         |  -------
         |  from_arrays
         |  from_tuples
         |  from_breaks
         |  contains
         |  overlaps
         |  set_closed
         |  to_tuples
         |
         |  See Also
         |  --------
         |  Index : The base pandas Index type.
         |  Interval : A bounded slice-like interval; the elements of an IntervalIndex.
         |  interval_range : Function to create a fixed frequency IntervalIndex.
         |  cut : Bin values into discrete Intervals.
         |  qcut : Bin values into equal-sized Intervals based on rank or sample quantiles.
         |
         |  Notes
         |  -----
         |  See the `user guide
         |  <https://pandas.pydata.org/pandas-docs/stable/user_guide/advanced.html#intervalindex>`_
         |  for more.
         |
         |  Examples
         |  --------
         |  A new ``IntervalIndex`` is typically constructed using
         |  :func:`interval_range`:

         |  >>> pd.interval_range(start=0, end=5)
         |  IntervalIndex([(0, 1], (1, 2], (2, 3], (3, 4], (4, 5]],
         |                closed='right',
         |                dtype='interval[int64]')

         |  It may also be constructed using one of the constructor
         |  methods: :meth:`IntervalIndex.from_arrays`,
         |  :meth:`IntervalIndex.from_breaks`, and :meth:`IntervalIndex.from_tuples`.

         |  See further examples in the doc strings of ``interval_range`` and the
         |  mentioned constructor methods.
         |
         |  Method resolution order:
         |      IntervalIndex
         |      pandas._libs.interval.IntervalMixin
         |      pandas.core.indexes.extension.ExtensionIndex
         |      pandas.core.indexes.base.Index
         |      pandas.core.base.IndexOpsMixin
         |      pandas.core.base.PandasObject
         |      pandas.core.accessor.DirNamesMixin
         |      builtins.object

        """
        """
        class Interval(IntervalMixin)
         |  Immutable object implementing an Interval, a bounded slice-like interval.
         |
         |  Parameters
         |  ----------
         |  left : orderable scalar
         |      Left bound for the interval.
         |  right : orderable scalar
         |      Right bound for the interval.
         |  closed : {'right', 'left', 'both', 'neither'}, default 'right'
         |      Whether the interval is closed on the left-side, right-side, both or
         |      neither. See the Notes for more detailed explanation.
         |
         |  See Also
         |  --------
         |  IntervalIndex : An Index of Interval objects that are all closed on the
         |      same side.
         |  cut : Convert continuous data into discrete bins (Categorical
         |      of Interval objects).
         |  qcut : Convert continuous data into bins (Categorical of Interval objects)
         |      based on quantiles.
         |  Period : Represents a period of time.
         |
         |  Notes
         |  -----
         |  The parameters `left` and `right` must be from the same type, you must be
         |  able to compare them and they must satisfy ``left <= right``.
         |
         |  A closed interval (in mathematics denoted by square brackets) contains
         |  its endpoints, i.e. the closed interval ``[0, 5]`` is characterized by the
         |  conditions ``0 <= x <= 5``. This is what ``closed='both'`` stands for.
         |  An open interval (in mathematics denoted by parentheses) does not contain
         |  its endpoints, i.e. the open interval ``(0, 5)`` is characterized by the
         |  conditions ``0 < x < 5``. This is what ``closed='neither'`` stands for.
         |  Intervals can also be half-open or half-closed, i.e. ``[0, 5)`` is
         |  described by ``0 <= x < 5`` (``closed='left'``) and ``(0, 5]`` is
         |  described by ``0 < x <= 5`` (``closed='right'``).
         |
         |  Examples
         |  --------
         |  It is possible to build Intervals of different types, like numeric ones:
         |
         |  >>> iv = pd.Interval(left=0, right=5)
         |  >>> iv
         |  Interval(0, 5, closed='right')
         |
         |  You can check if an element belongs to it
         |
         |  >>> 2.5 in iv
         |  True
         |
         |  You can syntax the bounds (``closed='right'``, so ``0 < x <= 5``):
         |
         |  >>> 0 in iv
         |  False
         |  >>> 5 in iv
         |  True
         |  >>> 0.0001 in iv
         |  True
         |
         |  Calculate its length
         |
         |  >>> iv.length
         |  5
         |
         |  You can operate with `+` and `*` over an Interval and the operation
         |  is applied to each of its bounds, so the result depends on the type
         |  of the bound elements
         |
         |  >>> shifted_iv = iv + 3
         |  >>> shifted_iv
         |  Interval(3, 8, closed='right')
         |  >>> extended_iv = iv * 10.0
         |  >>> extended_iv
         |  Interval(0.0, 50.0, closed='right')
         |
         |  To create a time interval you can use Timestamps as the bounds
         |
         |  >>> year_2017 = pd.Interval(pd.Timestamp('2017-01-01 00:00:00'),
         |  ...                         pd.Timestamp('2018-01-01 00:00:00'),
         |  ...                         closed='left')
         |  >>> pd.Timestamp('2017-01-01 00:00') in year_2017
         |  True
         |  >>> year_2017.length
         |  Timedelta('365 days 00:00:00')
         |
         |  And also you can create string intervals
         |
         |  >>> volume_1 = pd.Interval('Ant', 'Dog', closed='both')
         |  >>> 'Bee' in volume_1
         |  True
         |
         |  Method resolution order:
         |      Interval
         |      IntervalMixin
         |      builtins.object
        """
        """
        class IntervalDtype(PandasExtensionDtype)
         |  IntervalDtype(subtype=None)
         |
         |  An ExtensionDtype for Interval data.
         |
         |  **This is not an actual numpy dtype**, but a duck type.
         |
         |  Parameters
         |  ----------
         |  subtype : str, np.dtype
         |      The dtype of the Interval bounds.
         |
         |  Attributes
         |  ----------
         |  subtype
         |
         |  Methods
         |  -------
         |  None
         |
         |  Examples
         |  --------
         |  >>> pd.IntervalDtype(subtype='int64')
         |  interval[int64]
         |
         |  Method resolution order:
         |      IntervalDtype
         |      PandasExtensionDtype
         |      pandas.core.dtypes.base.ExtensionDtype
         |      builtins.object
        """
        # --- index1: closed='left'

        loc = locals()
        # fail to use exec: you may retrieve var from locals()
        index1_expr = \
            "interval_index1 = pd.IntervalIndex(\n" + \
            "    [pd.Interval(1, 2), pd.Interval(2, 3)], \n" + \
            "     closed='left', \n" + \
            "    dtype='interval[int]')"
        exec(index1_expr)
        interval_index1 = loc['interval_index1']

        print(f"{'-'*80}\n"
              f"{index1_expr}\n"
              " >>> interval_index1\n"
              f"{interval_index1}")

        # --- index2: closed='right'
        index2_expr = \
            "interval_index2 = pd.IntervalIndex([pd.Interval(1, 2), pd.Interval(2, 3)],\n" + \
            "                                   closed='right',\n" + \
            "                                   dtype=pd.IntervalDtype(float))\n"
        exec(index2_expr)
        interval_index2 = loc['interval_index2']
        print(f"{'-'*80}\n"
              " >>> interval_index2 = pd.IntervalIndex([pd.Interval(1, 2), pd.Interval(2, 3)],\n"
              "                                        closed='right',\n"
              "                                        dtype=pd.IntervalDtype(float))\n"
              " >>> interval_index2\n"
              f"{interval_index2}")

        # --- index3: use interval index to seek
        interval_index3 = pd.IntervalIndex([pd.Interval(1, 2), pd.Interval(2, 3)],
                                           closed='both',
                                           dtype=pd.IntervalDtype(np.float))

        def seek_interval(x, vi):
            return_srt = ''
            for v in vi:
                if x in v:
                    return_srt += '{} in {}\n'.format(x, v)
                else:
                    return_srt += '{} not in {}\n'.format(x, v)
            return return_srt

        print("{'-'*80}\n"
              " >>> interval_index3 = pd.IntervalIndex([pd.Interval(1, 2), pd.Interval(2, 3)],\n"
              "                                        closed='both',\n"
              "                                        dtype=pd.IntervalDtype(np.float))\n"
              " >>> interval_index3\n"
              f"{interval_index3}\n"
              " >>> def seek_interval(x, vi):\n"
              "         for v in interval_index2:\n"
              "             if x in v:\n"
              "                 print('2 in {}'.format(v))\n"
              "             else:\n"
              "                 print('2 not in {}'.format(v))\n"
              f"{seek_interval(2, interval_index3)}"
              )

        # ------ IntervalIndex in DataFrame
        names = ['He', 'Li', 'Mi']
        scores = [55, 75, 60]
        score_interval = pd.IntervalIndex(
            data=[pd.Interval(0, 59, closed='right'),
                  pd.Interval(60, 100, closed='left'),
                  pd.Interval(60, 100, closed='left')],
            closed='both',
            dtype=pd.IntervalDtype(int)
            )
        df = pd.DataFrame({'name': names, 'score': scores}, index=score_interval)
        print(f"{'-'*80}\n"
              f" >>> names = {names}\n"
              f" >>> scores = {scores}\n"
              " >>> score_interval = pd.IntervalIndex(\n"
              "         data=[pd.Interval(0, 59), pd.Interval(60, 100), pd.Interval(60, 100)],\n"
              "         closed='both',\n"
              "         dtype=pd.IntervalDtype(int))\n"
              " >>> df = pd.DataFrame({'name': names, 'score': scores}, index=score_interval)\n"
              " >>> df\n"
              f"{df}"
              )

    def index_interval_methods(self):
        # interval_range
        """
        interval_range(start=None, end=None, periods=None, freq=None, name=None, closed='right')

            Return a fixed frequency IntervalIndex.

            Parameters
            ----------
            start : numeric or datetime-like, default None
                Left bound for generating intervals.
            end : numeric or datetime-like, default None
                Right bound for generating intervals.
            periods : int, default None
                Number of periods to generate.
            freq : numeric, str, or DateOffset, default None
                The length of each interval. Must be consistent with the type of start
                and end, e.g. 2 for numeric, or '5H' for datetime-like.  Default is 1
                for numeric and 'D' for datetime-like.
            name : str, default None
                Name of the resulting IntervalIndex.
            closed : {'left', 'right', 'both', 'neither'}, default 'right'
                Whether the intervals are closed on the left-side, right-side, both
                or neither.

            Returns
            -------
            IntervalIndex

            See Also
            --------
            IntervalIndex : An Index of intervals that are all closed on the same side.

            Notes
            -----
            Of the four parameters ``start``, ``end``, ``periods``, and ``freq``,
            exactly three must be specified. If ``freq`` is omitted, the resulting
            ``IntervalIndex`` will have ``periods`` linearly spaced elements between
            ``start`` and ``end``, inclusively.

            To learn more about datetime-like frequency strings, please see `this link
            <https://pandas.pydata.org/pandas-docs/stable/user_guide/timeseries.html#offset-aliases>`__.

            Examples
            --------
            Numeric ``start`` and  ``end`` is supported.

            >>> pd.interval_range(start=0, end=5)
            IntervalIndex([(0, 1], (1, 2], (2, 3], (3, 4], (4, 5]],
                          closed='right', dtype='interval[int64]')

            Additionally, datetime-like input is also supported.

            >>> pd.interval_range(start=pd.Timestamp('2017-01-01'),
            ...                   end=pd.Timestamp('2017-01-04'))
            IntervalIndex([(2017-01-01, 2017-01-02], (2017-01-02, 2017-01-03],
                           (2017-01-03, 2017-01-04]],
                          closed='right', dtype='interval[datetime64[ns]]')

        """

        # from_arrays
        """
        from_arrays(left, right, closed: str = 'right', name=None, copy: bool = False, dtype=None) 
            method of builtins.type instance
            
            Construct from two arrays defining the left and right bounds.

            Parameters
            ----------
            left : array-like (1-dimensional)
                Left bounds for each interval.
            right : array-like (1-dimensional)
                Right bounds for each interval.
            closed : {'left', 'right', 'both', 'neither'}, default 'right'
                Whether the intervals are closed on the left-side, right-side, both
                or neither.
            copy : bool, default False
                Copy the data.
            dtype : dtype, optional
                If None, dtype will be inferred.

                .. versionadded:: 0.23.0

            Returns
            -------
            IntervalIndex
            Raises
            ------
            ValueError
                When a value is missing in only one of `left` or `right`.
                When a value in `left` is greater than the corresponding value
                in `right`.

            See Also
            --------
            interval_range : Function to create a fixed frequency IntervalIndex.
            IntervalIndex.from_breaks : Construct an IntervalIndex from an array of
                splits.
            IntervalIndex.from_tuples : Construct an IntervalIndex from an
                array-like of tuples.
            IntervalIndex.from_tuples : Construct an IntervalIndex from an
                array-like of tuples.

            Notes
            -----
            Each element of `left` must be less than or equal to the `right`
            element at the same position. If an element is missing, it must be
            missing in both `left` and `right`. A TypeError is raised when
            using an unsupported type for `left` or `right`. At the moment,
            'category', 'object', and 'string' subtypes are not supported.

            Examples
            --------
            >>> pd.IntervalIndex.from_arrays([0, 1, 2], [1, 2, 3])
            IntervalIndex([(0, 1], (1, 2], (2, 3]],
                          closed='right',
                          dtype='interval[int64]')
        """

        # from_tuples
        """
        from_tuples(data, closed: str = 'right', name=None, copy: bool = False, dtype=None) method of builtins.type instance
            Construct an IntervalIndex from an array-like of tuples.
        
            Parameters
            ----------
            data : array-like (1-dimensional)
                Array of tuples.
            closed : {'left', 'right', 'both', 'neither'}, default 'right'
                Whether the intervals are closed on the left-side, right-side, both
                or neither.
            copy : bool, default False
                By-default copy the data, this is compat only and ignored.
            dtype : dtype or None, default None
                If None, dtype will be inferred.
        
                .. versionadded:: 0.23.0
            Returns
            -------
            IntervalIndex
        
            See Also
            --------
            interval_range : Function to create a fixed frequency IntervalIndex.
            IntervalIndex.from_arrays : Construct an IntervalIndex from a left and
                                        right array.
            IntervalIndex.from_breaks : Construct an IntervalIndex from an array of
                                        splits.
        
            Examples
            --------
            >>> pd.IntervalIndex.from_tuples([(0, 1), (1, 2)])
            IntervalIndex([(0, 1], (1, 2]],
                           closed='right',
                           dtype='interval[int64]')

        """

        # from_breaks
        """
        from_breaks(breaks, closed: str = 'right', name=None, copy: bool = False, dtype=None) 
            method of builtins.type instance
            
            Construct an IntervalIndex from an array of splits.
        
            Parameters
            ----------
            breaks : array-like (1-dimensional)
                Left and right bounds for each interval.
            closed : {'left', 'right', 'both', 'neither'}, default 'right'
                Whether the intervals are closed on the left-side, right-side, both
                or neither.
            copy : bool, default False
                Copy the data.
            dtype : dtype or None, default None
                If None, dtype will be inferred.
        
                .. versionadded:: 0.23.0
            Returns
            -------
            IntervalIndex
        
            See Also
            --------
            interval_range : Function to create a fixed frequency IntervalIndex.
            IntervalIndex.from_arrays : Construct from a left and right array.
            IntervalIndex.from_tuples : Construct from a sequence of tuples.
        
            Examples
            --------
            >>> pd.IntervalIndex.from_breaks([0, 1, 2, 3])
            IntervalIndex([(0, 1], (1, 2], (2, 3]],
                          closed='right',
                          dtype='interval[int64]')
        """

        print("="*80)
        # ------ interval_range
        interval_range1 = pd.interval_range(start=1, end=6)
        interval_range2 = pd.interval_range(start=pd.Timestamp('2010-1-1'),
                                            end=pd.Timestamp('2010-1-3'))
        interval_range3 = pd.interval_range(start=1, end=6, periods=4)
        interval_range4 = pd.interval_range(start=1, end=6, freq=2)
        print(" >>> interval_range1 = pd.interval_range(start=1, end=6)\n"
              " >>> interval_range1\n"
              f"{interval_range1}\n"
              " >>> interval_range2 = pd.interval_range(start=pd.Timestamp('2010-1-1', \n"
              "                                   end=pd.Timestamp('2010-1-3'))\n"
              " >>> interval_range2\n"
              f"{interval_range2}\n"
              " >>> interval_range3 = pd.interval_range(start=1, end=6, periods=4)\n"
              " >>> interval_range3\n"
              f"{interval_range3}\n"
              " >>> interval_range4 = pd.interval_range(start=1, end=6, freq=2)\n"
              " >>> interval_range4\n"
              f"{interval_range4}"
              )

        # ------ from_arrays
        interval_arrays = pd.IntervalIndex.from_arrays(
            left=[1, 3, 5], right=[2, 4, 6],
            closed='both', name='interval', dtype=pd.IntervalDtype(int))
        print(f"{'-'*80}\n"
              " >>> interval_arrays = pd.IntervalIndex.from_arrays(\n"
              "         left=[1, 3, 5], right=[2, 4, 6],\n"
              "         closed='both', name='interval-1', dtype=pd.IntervalDtype(int))\n"
              " >>> interval_arrays\n"
              f"{interval_arrays}")

        # ------ from_tuples
        interval_tuples = pd.IntervalIndex.from_tuples(
            data=[(1, 3), (2, 4), (4, 6)],
            closed='neither', name='interval-2', dtype=pd.IntervalDtype(int))
        print(f"{'-'*80}\n"
              " >>> interval_tuples = pd.IntervalIndex.from_arrays(\n"
              "         data=[(1, 3), (2, 4), (4, 6)],\n"
              "         closed='neither', name='interval-2', dtype=pd.IntervalDtype(int))\n"
              " >>> interval_tuples\n"
              f"{interval_tuples}")

        # ------ from_breaks
        interval_breaks = pd.IntervalIndex.from_breaks(
            breaks=[1, 3, 4, 6],
            closed='left', name='interval-3', dtype=pd.IntervalDtype(float))
        print(f"{'-'*80}\n"
              ">>> interval_breaks = pd.IntervalIndex.from_breaks(\n"
              "         breaks=[1, 3, 4, 6],\n"
              "         closed='left', name='interval-3', dtype=pd.IntervalDtype(float))\n"
              " >>> interval_breaks\n"
              f"{interval_breaks}")

    def index_objects(self):
        # Int64Index
        """
        class Int64Index(IntegerIndex)
         |  Int64Index(data=None, dtype=None, copy=False, name=None)
         |
         |  Immutable ndarray implementing an ordered, sliceable set. The basic object
         |  storing axis labels for all pandas objects. Int64Index is a special case
         |  of `Index` with purely integer labels. .
         |
         |  Parameters
         |  ----------
         |  data : array-like (1-dimensional)
         |  dtype : NumPy dtype (default: int64)
         |  copy : bool
         |      Make a copy of input ndarray.
         |  name : object
         |      Name to be stored in the index.
        """
        int_index = pd.Int64Index(data=range(1, 10, 3), dtype=np.int16)
        print(" # 在Int64Index中, dtype只能使用整数类型int或Numpy兼容类型，如np.uint\n"
              " >>> int_index = pd.Int64Index(data=range(1, 10, 3), dtype=np.int16)\n"
              " >>> int_index\n"
              f"{int_index}\n"
              " >>> int_index[0:3]\n"
              f"{int_index[0:3]}")

        # UInt64Index
        """
        class UInt64Index(IntegerIndex)
         |  UInt64Index(data=None, dtype=None, copy=False, name=None)
         |
         |  Immutable ndarray implementing an ordered, sliceable set. The basic object
         |  storing axis labels for all pandas objects. UInt64Index is a special case
         |  of `Index` with purely unsigned integer labels. .
         |
         |  Parameters
         |  ----------
         |  data : array-like (1-dimensional)
         |  dtype : NumPy dtype (default: uint64)
         |  copy : bool
         |      Make a copy of input ndarray.
         |  name : object
         |      Name to be stored in the index.
         |
         |  Attributes
         |  ----------
         |  None
         |
         |  Methods
         |  -------
         |  None
        """
        print("="*80)
        uint_index = pd.UInt64Index(data=range(1, 10, 3), dtype=np.uint16)
        print(" # 在UInt64Index中, dtype只能使用Numpy无符号整数类型，如np.uint\n"
              " >>> int_index = pd.UInt64Index(data=range(1, 10, 3), dtype=np.uint16)\n"
              " >>> int_index\n"
              f"{uint_index}\n"
              " >>> uint_index[0:3]\n"
              f"{uint_index[0:3]}")

        # RangeIndex
        """
        class RangeIndex(pandas.core.indexes.numeric.Int64Index)
         |  RangeIndex(start=None, stop=None, step=None, dtype=None, copy=False, name=None)
         |
         |  Immutable Index implementing a monotonic integer range.
         |
         |  RangeIndex is a memory-saving special case of Int64Index limited to
         |  representing monotonic ranges. Using RangeIndex may in some instances
         |  improve computing speed.
         |
         |  This is the default index type used
         |  by DataFrame and Series when no explicit index is provided by the user.
         |
         |  Parameters
         |  ----------
         |  start : int (default: 0), or other RangeIndex instance
         |      If int and "stop" is not given, interpreted as "stop" instead.
         |  stop : int (default: 0)
         |  step : int (default: 1)
         |  name : object, optional
         |      Name to be stored in the index.
         |  copy : bool, default False
         |      Unused, accepted for homogeneity with other index types.
         |
         |  Attributes
         |  ----------
         |  start
         |  stop
         |  step
         |
         |  Methods
         |  -------
         |  from_range
         |
         |  See Also
         |  --------
         |  Index : The base pandas Index type.
         |  Int64Index : Index of int64 data.
         |      Examples
         |      --------
         |      **all**
         |
         |      True, because nonzero integers are considered True.
         |
         |      >>> pd.Index([1, 2, 3]).all()
         |      True
         |
         |      False, because ``0`` is considered False.
         |
         |      >>> pd.Index([0, 1, 2]).all()
         |      False
         |
         |      **any**
         |
         |      True, because ``1`` is considered True.
         |
         |      >>> pd.Index([0, 0, 1]).any()
         |      True
         |
         |      False, because ``0`` is considered False.
         |
         |      >>> pd.Index([0, 0, 0]).any()
         |      False
         |
         |  any(self) -> bool
         |      Return whether any element is True.
         |
         |      Parameters
         |      ----------
         |      *args
         |          These parameters will be passed to numpy.any.
         |      **kwargs
         |          These parameters will be passed to numpy.any.
         |
         |      Returns
         |      -------
         |      any : bool or array_like (if axis is specified)
         |          A single element array_like may be converted to bool.
         |
         |      See Also
         |      --------
         |      Index.all : Return whether all elements are True.
         |      Series.all : Return whether all elements are True.
         |
         |      Notes
         |      -----
         |      Not a Number (NaN), positive infinity and negative infinity
         |      evaluate to True because these are not equal to zero.
         |
         |      Examples
         |      --------
         |      >>> index = pd.Index([0, 1, 2])
         |      >>> index.any()
         |      True
         |
         |      >>> index = pd.Index([0, 0, 0])
         |      >>> index.any()
         |      False
         |
         |  argsort(self, *args, **kwargs) -> numpy.ndarray
         |      Returns the indices that would sort the index and its
         |      underlying data.
         |
         |      Returns
         |      -------
         |      argsorted : numpy array
         |
         |      See Also
         |      --------
         |      numpy.ndarray.argsort
         |
         |  copy(self, name=None, deep=False, dtype=None, **kwargs)
         |      Make a copy of this object.
         |
         |      Name and dtype sets those attributes on the new object.
         |
         |      Parameters
         |      ----------
         |      name : Label, optional
         |          Set name for new object.
         |      deep : bool, default False
         |      dtype : numpy dtype or pandas type, optional
         |          Set dtype for new object.
         |      names : list-like, optional
         |          Kept for compatibility with MultiIndex. Should not be used.
         |
         |      Returns
         |      -------
         |      Index
         |          Index refer to new object which is a copy of this object.
         |
         |      Notes
         |      -----
         |      In most cases, there should be no functional difference from using
         |      ``deep``, but if ``deep`` is passed it will attempt to deepcopy.
         |
         |  equals(self, other) -> bool
         |      Determines if two Index objects contain the same elements.
         |
         |  get_indexer(self, target, method=None, limit=None, tolerance=None)
         |      Compute indexer and mask for new index given the current index. The
         |      indexer should be then used as an input to ndarray.take to align the
         |      current data to the new index.
         |
         |      Parameters
         |      ----------
         |      target : %(target_klass)s
         |      method : {None, 'pad'/'ffill', 'backfill'/'bfill', 'nearest'}, optional
         |          * default: exact matches only.
         |          * pad / ffill: find the PREVIOUS index value if no exact match.
         |          * backfill / bfill: use NEXT index value if no exact match
         |          * nearest: use the NEAREST index value if no exact match. Tied
         |            distances are broken by preferring the larger index value.
         |      limit : int, optional
         |          Maximum number of consecutive labels in ``target`` to match for
         |          inexact matches.
         |      tolerance : optional
         |          Maximum distance between original and new labels for inexact
         |          matches. The values of the index at the matching locations most
         |          satisfy the equation ``abs(index[indexer] - target) <= tolerance``.
         |
         |          Tolerance may be a scalar value, which applies the same tolerance
         |          to all values, or list-like, which applies variable tolerance per
         |          element. List-like includes list, tuple, array, Series, and must be
         |          the same size as the index and its dtype must exactly match the
         |          index's type.
         |
         |      Returns
         |      -------
         |      indexer : ndarray of int
         |          Integers from 0 to n - 1 indicating that the index at these
         |          positions matches the corresponding target values. Missing values
         |          in the target are marked by -1.
         |      %(raises_section)s
         |      Examples
         |      --------
         |      >>> index = pd.Index(['c', 'a', 'b'])
         |      >>> index.get_indexer(['a', 'b', 'x'])
         |      array([ 1,  2, -1])
         |
         |      Notice that the return value is an array of locations in ``index``
         |      and ``x`` is marked by -1, as it is not in ``index``.
         |
         |  get_loc(self, key, method=None, tolerance=None)
         |      Get integer location, slice or boolean mask for requested label.
         |
         |      Parameters
         |      ----------
         |      key : label
         |      method : {None, 'pad'/'ffill', 'backfill'/'bfill', 'nearest'}, optional
         |          * default: exact matches only.
         |          * pad / ffill: find the PREVIOUS index value if no exact match.
         |          * backfill / bfill: use NEXT index value if no exact match
         |          * nearest: use the NEAREST index value if no exact match. Tied
         |            distances are broken by preferring the larger index value.
         |      tolerance : int or float, optional
         |          Maximum distance from index value for inexact matches. The value of
         |          the index at the matching location most satisfy the equation
         |          ``abs(index[loc] - key) <= tolerance``.
         |
         |      Returns
         |      -------
         |      loc : int if unique index, slice if monotonic index, else mask
         |
         |      Examples
         |      --------
         |      >>> unique_index = pd.Index(list('abc'))
         |      >>> unique_index.get_loc('b')
         |      1
         |
         |      >>> monotonic_index = pd.Index(list('abbc'))
         |      >>> monotonic_index.get_loc('b')
         |      slice(1, 3, None)
         |
         |      >>> non_monotonic_index = pd.Index(list('abcb'))
         |      >>> non_monotonic_index.get_loc('b')
         |      array([False,  True, False,  True])
         |
         |  intersection(self, other, sort=False)
         |      Form the intersection of two Index objects.
         |
         |      Parameters
         |      ----------
         |      other : Index or array-like
         |      sort : False or None, default False
         |          Sort the resulting index if possible
         |
         |          .. versionadded:: 0.24.0
         |
         |          .. versionchanged:: 0.24.1
         |
         |             Changed the default to ``False`` to match the behaviour
         |             from before 0.24.0.
         |
         |      Returns
         |      -------
         |      intersection : Index
         |
         |  join(self, other, how='left', level=None, return_indexers=False, sort=False)
         |      Compute join_index and indexers to conform data
         |      structures to the new index.
         |
         |      Parameters
         |      ----------
         |      other : Index
         |      how : {'left', 'right', 'inner', 'outer'}
         |      level : int or level name, default None
         |      return_indexers : bool, default False
         |      sort : bool, default False
         |          Sort the join keys lexicographically in the result Index. If False,
         |          the order of the join keys depends on the join type (how keyword).
         |
         |      Returns
         |      -------
         |      join_index, (left_indexer, right_indexer)

        """
        print("="*80)
        range_index = pd.RangeIndex(start=1, stop=10, step=2)
        sr = pd.Series(range(5), index=range_index)
        print(" # 创建RangeIndex索引对象\n"
              " >>> range_index = pd.RangeIndex(start=1, stop=10, step=2)\n"
              " >>> range_index\n"
              f"{range_index}\n"
              " >>> range_index.values\n"
              f"{range_index.values}\n"
              " # 在Series数据集中使用RangeIndex索引\n"
              " >>> sr = pd.Series(range(5), index=range_index)\n"
              " >>> sr\n"
              f"{sr}\n"
              " # 在RangeIndex索引的数据集中使用切片时，使用位置访问\n"
              f"{sr[0:3]}")

        # Float64Index
        """
        class Float64Index(NumericIndex)
         |  Float64Index(data=None, dtype=None, copy=False, name=None)
         |
         |  Immutable ndarray implementing an ordered, sliceable set. The basic object
         |  storing axis labels for all pandas objects. Float64Index is a special case
         |  of `Index` with purely float labels. .
         |
         |  Parameters
         |  ----------
         |  data : array-like (1-dimensional)
         |  dtype : NumPy dtype (default: float64)
         |  copy : bool
         |      Make a copy of input ndarray.
         |  name : object
         |      Name to be stored in the index.
         |
         |  Attributes
         |  ----------
         |  None
         |
         |  Methods
         |  -------
         |  None
         |
        """
        float_index = pd.Float64Index(data=range(1, 10, 3), dtype=np.float16)
        print("="*80)
        print(" # 在Float64Index中, dtype只能使用浮点类型float或Numpy浮点类型，如np.float16\n"
              " >>> float_index = pd.Float64Index(data=range(1, 10, 3), dtype=np.float16)\n"
              " >>> float_index\n"
              f"{float_index}\n"
              " # 在浮点类型索引中，使用切片是按位置访问\n"
              " >>> float_index[0:3]\n"
              f"{float_index[0:3]}")

        print('-.'*40)
        sr = pd.Series(range(3), index=float_index)
        print(" # 在浮点类型索引的数据集中，使用切片是按标签值访问\n"
              " >>> sr = pd.Series(range(3), index=float_index)\n"
              f"{sr}\n"
              f"{sr.index}\n"
              " >>> sr[0:3]\n"
              f"{sr[0:3]}")

    def index_multiindex(self):
        """
        MultiIndex(levels=None, codes=None, sortorder=None, names=None, dtype=None,
                    copy=False, name=None, verify_integrity=True)
        name: no commets in authoritative document, may mean that is disabled augment
        codes: Changed in version 0.24.0: MultiIndex.labels has been renamed to MultiIndex.codes
               and MultiIndex.set_labels to MultiIndex.set_codes.
               must use labels in earlier version
        """

        # MultiIndex1: 使用构建方法生成多层索引
        levels_list = [['Xu', 'He'], [1, 2, 3]]
        codes_list = [[0, 0, 1, 1, 1], [0, 1, 0, 1, 2]]
        mindex = pd.MultiIndex(levels=levels_list,
                               codes=codes_list,
                               names=['name', 'grade']
                               )
        sr1 = pd.Series(data=[75, 80, 64, 88, 93], index=mindex)
        print(
              f"{'-'*80}\n"
              "# 使用构建方法, 由多个数组进行分层标签编码，生成多层索引\n"
              " >>> level_list = [['Xu', 'He'], [1, 2, 3]]\n"
              " >>> code_list = [[0, 0, 1, 1, 1], [0, 1, 0, 1, 2]]\n"
              " >>> mindex = pd.MultiIndex(\n"
              "         levels=level_list,\n"
              "         codes=code_list,\n"
              "         names=['name', 'grade'])\n"
              " >>> mindex\n"
              f"{mindex}\n"
              " >>> sr1 = pd.Series(data=[75, 80, 64, 88, 93], index=mindex)\n"
              " >>> sr1\n"
              f"{sr1}"
              f" >>> mindex.levels\n"
              )

        # MultiIndex2：使用from_product生成多层索引
        # df1 = pd.DataFrame(
        #     data={'语文': [random.randint(60, 100) for _ in range(12)],
        #           '数学': [random.randint(60, 100) for _ in range(12)]},
        #     index=pd.MultiIndex.from_product([['李明', '张乐', '何婷'], ['初考', '作业', '例考', '终考']]),
        #     dtype=np.uint8)
        # print(
        #     f"{'-' * 80}\n"
        #     "MultiIndex: 使用方法from_product生成多层索引\n"
        #     " >>> df1 = pd.DataFrame(\n"
        #     "         data={'语文': [random.randint(60, 100) for _ in range(12)],\n"
        #     "               '数学': [random.randint(60, 100) for _ in range(12)]},\n"
        #     "         index=pd.MultiIndex.from_product(\n"
        #     "                   [['李明', '张乐', '何婷'], \n"
        #     "                    ['初考', '作业', '例考', '终考']]),\n"
        #     "         dtype=np.uint8"
        #     "         )\n"
        #     # " >>> df1.index\n"
        #     # f"{df1.index}\n"
        #     " >>> df1\n"
        #     f"{df1}\n"
        #     )

        sr2 = pd.Series(range(5), index=[[1, 1, 2, 2, 2], ['a', 'b', 'a', 'a', 'b'], ['r', 's', 'r', 's', 't']])
        print(
              f"{'-'*80}\n"
              "# 由数组直接生成多层索引\n"
              " >>> srm = pd.Series(range(5), \n"
              "             index=[[1, 1, 2, 2, 2], \n"
              "                    ['a', 'b', 'a', 'a', 'b'], \n"
              "                    ['r', 's', 'r', 's', 't']])\n"
              " >>> srm\n"
              f"{sr2}\n"
              "# 使用索引切片分层提取数据\n"
              " >>> sr2[:, :, 'r']\n"
              f"{sr2[:, :, 'r']}\n"
              " >>> sr2[:, 'b', :]\n"
              f"{sr2[:, 'b', :]}\n"
              # " >>> srm.unstack(levels=[0, 2], fill_value=200)\n"
              # f"{srm.unstack(level=[0, 2], fill_value=200)}"
              )
        return mindex

    def index_multiindex_from(self):
        # MultiIndex.from_arrays
        """
        from_arrays(arrays, sortorder=None, names=<object object at 0x000001F05E5DE690>)
            
            -> 'MultiIndex' method of builtins.type instance
            
            Convert arrays to MultiIndex.
        
            Parameters
            ----------
            arrays : list / sequence of array-likes
                Each array-like gives one level's value for each data point.
                len(arrays) is the number of levels.
            sortorder : int or None
                Level of sortedness (must be lexicographically sorted by that
                level).
            names : list / sequence of str, optional
                Names for the levels in the index.
                Level of sortedness (must be lexicographically sorted by that
                level).
            names : list / sequence of str, optional
                Names for the levels in the index.
        
            Returns
            -------
            MultiIndex
        
            See Also
            --------
            MultiIndex.from_tuples : Convert list of tuples to MultiIndex.
            MultiIndex.from_product : Make a MultiIndex from cartesian product
                                      of iterables.
            MultiIndex.from_frame : Make a MultiIndex from a DataFrame.
        
            Examples
            --------
            >>> arrays = [[1, 1, 2, 2], ['red', 'blue', 'red', 'blue']]
            >>> pd.MultiIndex.from_arrays(arrays, names=('number', 'color'))
            MultiIndex([(1,  'red'),
                        (1, 'blue'),
                        (2,  'red'),
                        (2, 'blue')],
                       names=['number', 'color'])
        """
        mindex_arrays = pd.MultiIndex.from_arrays(
            [['a', 'a', 'b', 'b', 'c'], [1, 2, 1, 2, 1]]
        )
        print(
              "# 使用方法from_arrays, 由多个相同长度数组对应元素联立, 生成多层索引\n"
              " >>> mindex_arrays = pd.MultiIndex.from_arrays(\n"
              "         [['a', 'a', 'b', 'b', 'c'], [1, 2, 1, 2, 1]]\n"
              "         )\n"
              " >>> mindex_arrays\n"
              f"{mindex_arrays}"
              )

        # from_product
        """
        from_product(iterables, sortorder=None, names=<object object at 0x000001F05E5DE690>) 
            method of builtins.type instance
            Make a MultiIndex from the cartesian product of multiple iterables.
        
            Parameters
            ----------
            iterables : list / sequence of iterables
                Each iterable has unique labels for each level of the index.
            sortorder : int or None
                Level of sortedness (must be lexicographically sorted by that
                level).
            names : list / sequence of str, optional
                Names for the levels in the index.
        
                .. versionchanged:: 1.0.0
        
                   If not explicitly provided, names will be inferred from the
                   elements of iterables if an element has a name attribute
        
            Returns
            -------
            MultiIndex
        
            See Also
            --------
            MultiIndex.from_arrays : Convert list of arrays to MultiIndex.
            MultiIndex.from_tuples : Convert list of tuples to MultiIndex.
            MultiIndex.from_frame : Make a MultiIndex from a DataFrame.
        
            Examples
            --------
            >>> numbers = [0, 1, 2]
            >>> colors = ['green', 'purple']
            >>> pd.MultiIndex.from_product([numbers, colors],
            ...                            names=['number', 'color'])
            MultiIndex([(0,  'green'),
                        (0, 'purple'),
                        (1,  'green'),
                        (1, 'purple'),
                        (2,  'green'),
                        (2, 'purple')],
                       names=['number', 'color'])
        """
        mindex_product = pd.MultiIndex.from_product(
            [['a', 'b', 'c'], ['1', '2']]
        )
        print(
              f"{'-' * 80}\n"
              "# 使用方法from_product， 由多个序列进行乘积，生成多层次索引\n"
              " >>> mindex_product = pd.MultiIndex.from_product(\n"
              "         ['a', 'b', 'c'], ['1', '2']\n"
              "         )\n"
              " >>> mindex_product\n"
              f"{mindex_product}"
              )

        # from_tuples
        """
        from_tuples(tuples, sortorder=None, names=None) method of builtins.type instance
            Convert list of tuples to MultiIndex.
        
            Parameters
            ----------
            tuples : list / sequence of tuple-likes
                Each tuple is the index of one row/column.
            sortorder : int or None
                Level of sortedness (must be lexicographically sorted by that
                level).
            names : list / sequence of str, optional
                Names for the levels in the index.
        
            Returns
            -------
            MultiIndex
        
            See Also
            --------
            MultiIndex.from_arrays : Convert list of arrays to MultiIndex.
            MultiIndex.from_product : Make a MultiIndex from cartesian product
            MultiIndex.from_frame : Make a MultiIndex from a DataFrame.
        
            Examples
            --------
            >>> tuples = [(1, 'red'), (1, 'blue'),
            ...           (2, 'red'), (2, 'blue')]
            >>> pd.MultiIndex.from_tuples(tuples, names=('number', 'color'))
            MultiIndex([(1,  'red'),
                        (1, 'blue'),
                        (2,  'red'),
                        (2, 'blue')],
                       names=['number', 'color'])
        """
        mindex_tuples = pd.MultiIndex.from_tuples([('a', 1), ('a', 2), ('b', 1), ('b', 2), ('c', 1)])
        print(
              f"{'-' * 80}\n"
              f"# 使用方法from_tuples， 由相同长度的元组数据作为标签， 生成多层索引\n"
              " >>> mindex_tuples = pd.MultiIndex.from_tuples(\n"
              "         [('a', 1), ('a', 2), ('b', 1), ('b', 2), ('c', 1)])\n"
              " >>> mindex_tuples\n"
              f"{mindex_tuples}"
              )

        # from_frame
        """
        from_frame(df, sortorder=None, names=None) 
            
            method of builtins.type instance
            
            Make a MultiIndex from a DataFrame.
        
            .. versionadded:: 0.24.0
        
            Parameters
            ----------
            df : DataFrame
                DataFrame to be converted to MultiIndex.
            sortorder : int, optional
                Level of sortedness (must be lexicographically sorted by that
                level).
            names : list-like, optional
                If no names are provided, use the column names, or tuple of column
                names if the columns is a MultiIndex. If a sequence, overwrite
                names with the given sequence.
        
            Returns
            -------
            MultiIndex
                The MultiIndex representation of the given DataFrame.
        
            See Also
            --------
            MultiIndex.from_arrays : Convert list of arrays to MultiIndex.
            MultiIndex.from_tuples : Convert list of tuples to MultiIndex.
            MultiIndex.from_product : Make a MultiIndex from cartesian product
                                      of iterables.
        
            Examples
            --------
            >>> df = pd.DataFrame([['HI', 'Temp'], ['HI', 'Precip'],
            ...                    ['NJ', 'Temp'], ['NJ', 'Precip']],
            ...                   columns=['a', 'b'])
            >>> df
                  a       b
            0    HI    Temp
            1    HI  Precip
            2    NJ    Temp
            3    NJ  Precip
        
            >>> pd.MultiIndex.from_frame(df)
            MultiIndex([('HI',   'Temp'),
                        ('HI', 'Precip'),
                        ('NJ',   'Temp'),
                        ('NJ', 'Precip')],
                       names=['a', 'b'])
        
            Using explicit names, instead of the column names
        
            >>> pd.MultiIndex.from_frame(df, names=['state', 'observation'])
            MultiIndex([('HI',   'Temp'),
                        ('HI', 'Precip'),
                        ('NJ',   'Temp'),
                        ('NJ', 'Precip')],
                       names=['state', 'observation'])
        """
        df = pd.DataFrame({'level-1': ['a', 'a', 'b'],
                           'levle-2': ['m', 'n', 'm'],
                           'levle-3': [1, 2, 1]})
        mindex_frame = pd.MultiIndex.from_frame(df)
        print(
              f"{'-' * 80}\n"
              f"# 使用方法from_frame， 由DataFrame数据集的各个列为层次标签，生成多层索引\n"
              " >>> df\n"
              f"{df}\n"
              " >>> mindex_frame = pd.MultiIndex.from_frame(df)\n"
              " >>> mindex_frame\n"
              f"{mindex_frame}"
              )

    def index_set_index(self):
        """
        set_index(self, keys, drop=True, append=False, inplace=False, verify_integrity=False)
            Set the DataFrame index using existing columns.

            Set the DataFrame index (row labels) using one or more existing
            columns or arrays (of the correct length). The index can replace the
            existing index or expand on it.

            Parameters
            ----------
            keys : label or array-like or list of labels/arrays
                This parameter can be either a single column key, a single array of
                the same length as the calling DataFrame, or a list containing an
                arbitrary combination of column keys and arrays. Here, "array"
                encompasses :class:`Series`, :class:`Index`, ``np.ndarray``, and
                instances of :class:`~collections.abc.Iterator`.
            drop : bool, default True
                Delete columns to be used as the new index.
            append : bool, default False
                Whether to append columns to existing index.
            inplace : bool, default False
                Modify the DataFrame in place (do not create a new object).
            verify_integrity : bool, default False
                Check the new index for duplicates. Otherwise defer the check until
                necessary. Setting to False will improve the performance of this
                method.

            Returns
            -------
            DataFrame
                Changed row labels.

            See Also
            --------
            DataFrame.reset_index : Opposite of set_index.
            DataFrame.reindex : Change to new indices or expand indices.
            DataFrame.reindex_like : Change to same indices as other DataFrame.

            Examples
            --------
            >>> df = pd.DataFrame({'month': [1, 4, 7, 10],
            ...                    'year': [2012, 2014, 2013, 2014],
            ...                    'sale': [55, 40, 84, 31]})
            >>> df
               month  year  sale
            0      1  2012    55
            1      4  2014    40
            2      7  2013    84
            3     10  2014    31

            Set the index to become the 'month' column:

            >>> df.set_index('month')
                   year  sale
            month
            1      2012    55
            4      2014    40
            7      2013    84
            10     2014    31

            Create a MultiIndex using columns 'year' and 'month':

            >>> df.set_index(['year', 'month'])
                        sale
            year  month
            2012  1     55
            2014  4     40
            2013  7     84
            2014  10    31

            Create a MultiIndex using an Index and a column:

            >>> df.set_index([pd.Index([1, 2, 3, 4]), 'year'])
                     month  sale
               year
            1  2012  1      55
            2  2014  4      40
            3  2013  7      84
            4  2014  10     31

            Create a MultiIndex using two Series:

            >>> s = pd.Series([1, 2, 3, 4])
            >>> df.set_index([s, s**2])
                  month  year  sale
            1 1       1  2012    55
            2 4       4  2014    40
            3 9       7  2013    84
            4 16     10  2014    31
        """
        df = self.df1.add(pd.Series([1, 2, 3], index=pd.date_range('2010.1.1', periods=3)), axis=0)
        print('DataFrame: df')
        print(df)

        print("\ndf.set_index('A')")
        print(df.set_index('A'))

        print("\ndf.set_index('A', drop=False)")
        print(df.set_index('A', drop=False))

        print("\ndf.set_index('A')")
        print(df.set_index('A'))

        print("\ndf.set_index(['A', 'B'])")
        print(df.set_index(['A', 'B']))

        print("\ndf.set_index(keys=[pd.Index(['math', 'art', 'log'], name='ind'), 'B'])")
        ind = pd.Index(['math', 'art', 'log'], name='ind')
        print(df.set_index(keys=[ind, 'B']))

        print("\ndf.set_index(keys=[pd.Series(['math', 'art', 'log'], name='ser'), 'B'])")
        ser = pd.Series(['math', 'art', 'log'], name='ser')
        print(df.set_index(keys=[ser, 'B']))

        print("\ndf.set_index('A', append=True)")
        df5 = df.set_index('A', append=True)
        print(df5, '\n', df5.index)

        print("\ndf5.reset_index(level=1)")
        df6 = df5.reset_index(level=1)
        print(df6)

        print("\ndf5.reset_index(level=1, drop=True)")
        df6 = df5.reset_index(level=1, drop=True)
        print(df6)

        print('\nSeries: sr')
        sr = pd.Series([3, 5, 4, 2],
                       index=pd.MultiIndex.from_product([['math', 'science'], ['test1', 'test2']]),
                       )
        print(sr)

        print("\nsr.reset_index(level=1)")
        print(sr.reset_index(level=1))

        print("\nsr.reset_index(level=1, name='syntax')")
        print(sr.reset_index(level=1, name='syntax'))

        print("\nsr.reset_index(level=1), sr.name='myseries'")
        sr.name = 'myseries'
        sr.index.names = ['course', 'syntax']
        print(sr.reset_index(level=1))

        print("\nsr.reset_index(level=1, name='score')")
        print(sr.reset_index(level=1, name='score'))

        print("\nsr.reset_index(level=1, drop=True)")
        print(sr.reset_index(level=1, drop=True))

    def index_reset_index(self):
        """
        reset_index(self, level: Union[Hashable, Sequence[Hashable], NoneType] = None, drop: bool = False,
                    inplace: bool = False, col_level: Hashable = 0, col_fill: Union[Hashable, NoneType] = '')
                     -> Union[ForwardRef('DataFrame'), NoneType]

            重置索引，或索引的一个层次
            Reset the index, or a level of it.

            Reset the index of the DataFrame, and use the default one instead.
            If the DataFrame has a MultiIndex, this method can remove one or more
            levels.

            Parameters
            ----------
            level : int, str, tuple, or list, default None
                Only remove the given levels from the index. Removes all levels by
                default.
            drop : bool, default False
                Do not try to insert index into dataframe columns. This resets
                the index to the default integer index.
            inplace : bool, default False
                Modify the DataFrame in place (do not create a new object).
            col_level : int or str, default 0
                If the columns have multiple levels, determines which level the
                labels are inserted into. By default it is inserted into the first
                level.
            col_fill : object, default ''
                If the columns have multiple levels, determines how the other
                levels are named. If None then the index name is repeated.

            Returns
            -------
            DataFrame or None
                DataFrame with the new index or None if ``inplace=True``.

            See Also
            --------
            DataFrame.set_index : Opposite of reset_index.
            DataFrame.reindex : Change to new indices or expand indices.
            DataFrame.reindex_like : Change to same indices as other DataFrame.

            Examples
            --------
            >>> df = pd.DataFrame([('bird', 389.0),
            ...                    ('bird', 24.0),
            ...                    ('mammal', 80.5),
            ...                    ('mammal', np.nan)],
            ...                   index=['falcon', 'parrot', 'lion', 'monkey'],
            ...                   columns=('class', 'max_speed'))
            >>> df
                     class  max_speed
            falcon    bird      389.0
            parrot    bird       24.0
            lion    mammal       80.5
            monkey  mammal        NaN

            When we reset the index, the old index is added as a column, and a
            new sequential index is used:

            >>> df.reset_index()
                index   class  max_speed
            0  falcon    bird      389.0
            1  parrot    bird       24.0
            2    lion  mammal       80.5
            3  monkey  mammal        NaN

            We can use the `drop` parameter to avoid the old index being added as
            a column:

            >>> df.reset_index(drop=True)
                class  max_speed
            0    bird      389.0
            1    bird       24.0
            2  mammal       80.5
            3  mammal        NaN

            You can also use `reset_index` with `MultiIndex`.

            >>> index = pd.MultiIndex.from_tuples([('bird', 'falcon'),
            ...                                    ('bird', 'parrot'),
            ...                                    ('mammal', 'lion'),
            ...                                    ('mammal', 'monkey')],
            ...                                   names=['class', 'name'])
            >>> columns = pd.MultiIndex.from_tuples([('speed', 'max'),
            ...                                      ('species', 'type')])
            >>> df = pd.DataFrame([(389.0, 'fly'),
            ...                    ( 24.0, 'fly'),
            ...                    ( 80.5, 'run'),
            ...                    (np.nan, 'jump')],
            ...                   index=index,
            ...                   columns=columns)
            >>> df
                           speed species
                             max    type
            class  name
            bird   falcon  389.0     fly
                   parrot   24.0     fly
            mammal lion     80.5     run
                   monkey    NaN    jump

            If the index has multiple levels, we can reset a subset of them:

            >>> df.reset_index(level='class')
                     class  speed species
                              max    type
            name
            falcon    bird  389.0     fly
            parrot    bird   24.0     fly
            lion    mammal   80.5     run
            monkey  mammal    NaN    jump

            If we are not dropping the index, by default, it is placed in the top
            level. We can place it in another level:

            >>> df.reset_index(level='class', col_level=1)
                            speed species
                     class    max    type
            name
            falcon    bird  389.0     fly
            parrot    bird   24.0     fly
            lion    mammal   80.5     run
            monkey  mammal    NaN    jump

            When the index is inserted under another level, we can specify under
            which one with the parameter `col_fill`:

            >>> df.reset_index(level='class', col_level=1, col_fill='species')
                          species  speed species
                            class    max    type
            name
            falcon           bird  389.0     fly
            parrot           bird   24.0     fly
            lion           mammal   80.5     run
            monkey         mammal    NaN    jump

            If we specify a nonexistent level for `col_fill`, it is created:

            >>> df.reset_index(level='class', col_level=1, col_fill='genus')
                            genus  speed species
                            class    max    type
            name
            falcon           bird  389.0     fly
            parrot           bird   24.0     fly
            lion           mammal   80.5     run
            monkey         mammal    NaN    jump
        """

    def index_reindex(self):
        """
        reindex(self, labels=None, index=None, columns=None, axis=None, method=None, copy=True, level=None,
                fill_value=nan, limit=None, tolerance=None)

            Conform Series/DataFrame to new index with optional filling logic.

            Places NA/NaN in locations having no value in the previous index. A new object
            is produced unless the new index is equivalent to the current one and
            ``copy=False``.

            Parameters
            ----------

            keywords for axes : array-like, optional
                New labels / index to conform to, should be specified using
                keywords. Preferably an Index object to avoid duplicating data.

            method : {None, 'backfill'/'bfill', 'pad'/'ffill', 'nearest'}
                Method to use for filling holes in reindexed DataFrame.
                Please note: this is only applicable to DataFrames/Series with a
                monotonically increasing/decreasing index.

                * None (default): don't fill gaps
                * pad / ffill: Propagate last valid observation forward to next
                  valid.
                * backfill / bfill: Use next valid observation to fill gap.
                * nearest: Use nearest valid observations to fill gap.

            copy : bool, default True
                Return a new object, even if the passed indexes are the same.
            level : int or name
                Broadcast across a level, matching Index values on the
                passed MultiIndex level.
            fill_value : scalar, default np.NaN
                Value to use for missing values. Defaults to NaN, but can be any
                "compatible" value.
            limit : int, default None
                Maximum number of consecutive elements to forward or backward fill.
            tolerance : optional
                Maximum distance between original and new labels for inexact
                matches. The values of the index at the matching locations most
                satisfy the equation ``abs(index[indexer] - target) <= tolerance``.

                Tolerance may be a scalar value, which applies the same tolerance
                to all values, or list-like, which applies variable tolerance per
                element. List-like includes list, tuple, array, Series, and must be
                the same size as the index and its dtype must exactly match the
                index's type.

            Returns
            -------
            Series/DataFrame with changed index.

            See Also
            --------
            DataFrame.set_index : Set row labels.
            DataFrame.reset_index : Remove row labels or move them to new columns.
            DataFrame.reindex_like : Change to same indices as other DataFrame.

            Examples
            --------
            ``DataFrame.reindex`` supports two calling conventions

            * ``(index=index_labels, columns=column_labels, ...)``
            * ``(labels, axis={'index', 'columns'}, ...)``

            We *highly* recommend using keyword arguments to clarify your
            intent.

            Create a dataframe with some fictional data.

            >>> index = ['Firefox', 'Chrome', 'Safari', 'IE10', 'Konqueror']
            >>> df = pd.DataFrame({'http_status': [200, 200, 404, 404, 301],
            ...                   'response_time': [0.04, 0.02, 0.07, 0.08, 1.0]},
            ...                   index=index)
            >>> df
                       http_status  response_time
            Firefox            200           0.04
            Chrome             200           0.02
            Safari             404           0.07
            IE10               404           0.08
            Konqueror          301           1.00

            Create a new index and reindex the dataframe. By default
            values in the new index that do not have corresponding
            records in the dataframe are assigned ``NaN``.

            >>> new_index = ['Safari', 'Iceweasel', 'Comodo Dragon', 'IE10',
            ...              'Chrome']
            >>> df.reindex(new_index)
                           http_status  response_time
            Safari               404.0           0.07
            Iceweasel              NaN            NaN
            Comodo Dragon          NaN            NaN
            IE10                 404.0           0.08
            Chrome               200.0           0.02

            We can fill in the missing values by passing a value to
            the keyword ``fill_value``. Because the index is not monotonically
            increasing or decreasing, we cannot use arguments to the keyword
            ``method`` to fill the ``NaN`` values.

            >>> df.reindex(new_index, fill_value=0)
                           http_status  response_time
            Safari                 404           0.07
            Iceweasel                0           0.00
            Comodo Dragon            0           0.00
            IE10                   404           0.08
            Chrome                 200           0.02

            >>> df.reindex(new_index, fill_value='missing')
                          http_status response_time
            Safari                404          0.07
            Iceweasel         missing       missing
            Comodo Dragon     missing       missing
            IE10                  404          0.08
            Chrome                200          0.02

            We can also reindex the columns.

            >>> df.reindex(columns=['http_status', 'user_agent'])
                       http_status  user_agent
            Firefox            200         NaN
            Chrome             200         NaN
            Safari             404         NaN
            IE10               404         NaN
            Konqueror          301         NaN

            Or we can use "axis-style" keyword arguments

            >>> df.reindex(['http_status', 'user_agent'], axis="columns")
                       http_status  user_agent
            Firefox            200         NaN
            Chrome             200         NaN
            Safari             404         NaN
            IE10               404         NaN
            Konqueror          301         NaN

            To further illustrate the filling functionality in
            ``reindex``, we will create a dataframe with a
            monotonically increasing index (for example, a sequence
            of dates).

            >>> date_index = pd.date_range('1/1/2010', periods=6, freq='D')
            >>> df2 = pd.DataFrame({"prices": [100, 101, np.nan, 100, 89, 88]},
            ...                    index=date_index)
            >>> df2
                        prices
            2010-01-01   100.0
            2010-01-02   101.0
            2010-01-03     NaN
            2010-01-04   100.0
            2010-01-05    89.0
            2010-01-06    88.0

            Suppose we decide to expand the dataframe to cover a wider
            date range.

            >>> date_index2 = pd.date_range('12/29/2009', periods=10, freq='D')
            >>> df2.reindex(date_index2)
                        prices
            2009-12-29     NaN
            2009-12-30     NaN
            2009-12-31     NaN
            2010-01-01   100.0
            2010-01-02   101.0
            2010-01-03     NaN
            2010-01-04   100.0
            2010-01-05    89.0
            2010-01-06    88.0
            2010-01-07     NaN

            The index entries that did not have a value in the original data frame
            (for example, '2009-12-29') are by default filled with ``NaN``.
            If desired, we can fill in the missing values using one of several
            options.

            For example, to back-propagate the last valid value to fill the ``NaN``
            values, pass ``bfill`` as an argument to the ``method`` keyword.

            >>> df2.reindex(date_index2, method='bfill')
                        prices
            2009-12-29   100.0
            2009-12-30   100.0
            2009-12-31   100.0
            2010-01-01   100.0
            2010-01-02   101.0
            2010-01-03     NaN
            2010-01-04   100.0
            2010-01-05    89.0
            2010-01-06    88.0
            2010-01-07     NaN

            Please note that the ``NaN`` value present in the original dataframe
            (at index value 2010-01-03) will not be filled by any of the
            value propagation schemes. This is because filling while reindexing
            does not look at dataframe values, but only compares the original and
            desired indexes. If you do want to fill in the ``NaN`` values present
            in the original dataframe, use the ``fillna()`` method.

            See the :obj:`user guide <basics.reindexing>` for more.

        """

        # Series.reindex
        """
        reindex(self, index=None, **kwargs)
            Conform Series to new index with optional filling logic.
        
            Places NA/NaN in locations having no value in the previous index. A new object
            is produced unless the new index is equivalent to the current one and
            ``copy=False``.
        
            Parameters
            ----------
        
            index : array-like, optional
                New labels / index to conform to, should be specified using
                keywords. Preferably an Index object to avoid duplicating data.
        
            method : {None, 'backfill'/'bfill', 'pad'/'ffill', 'nearest'}
                Method to use for filling holes in reindexed DataFrame.
                Please note: this is only applicable to DataFrames/Series with a
                monotonically increasing/decreasing index.
        
                * None (default): don't fill gaps
                * pad / ffill: Propagate last valid observation forward to next
                  valid.
                * backfill / bfill: Use next valid observation to fill gap.
                * nearest: Use nearest valid observations to fill gap.
        
            copy : bool, default True
                Return a new object, even if the passed indexes are the same.
            level : int or name
                Broadcast across a level, matching Index values on the
                passed MultiIndex level.
            fill_value : scalar, default np.NaN
                Value to use for missing values. Defaults to NaN, but can be any
                "compatible" value.
            limit : int, default None
                Maximum number of consecutive elements to forward or backward fill.
            tolerance : optional
                Maximum distance between original and new labels for inexact
                matches. The values of the index at the matching locations most
                satisfy the equation ``abs(index[indexer] - target) <= tolerance``.
        
                Tolerance may be a scalar value, which applies the same tolerance
                to all values, or list-like, which applies variable tolerance per
                element. List-like includes list, tuple, array, Series, and must be
                the same size as the index and its dtype must exactly match the
                index's type.
        
            Returns
            -------
            Series with changed index.
        
            See Also
            --------
            DataFrame.set_index : Set row labels.
            DataFrame.reset_index : Remove row labels or move them to new columns.
            DataFrame.reindex_like : Change to same indices as other DataFrame.
        
            Examples
            --------
            ``DataFrame.reindex`` supports two calling conventions
        
            * ``(index=index_labels, columns=column_labels, ...)``
            * ``(labels, axis={'index', 'columns'}, ...)``
        
            We *highly* recommend using keyword arguments to clarify your
            intent.
        
            Create a dataframe with some fictional data.
        
            >>> index = ['Firefox', 'Chrome', 'Safari', 'IE10', 'Konqueror']
            >>> df = pd.DataFrame({'http_status': [200, 200, 404, 404, 301],
            ...                   'response_time': [0.04, 0.02, 0.07, 0.08, 1.0]},
            ...                   index=index)
            >>> df
                       http_status  response_time
            Firefox            200           0.04
            Chrome             200           0.02
            Safari             404           0.07
            IE10               404           0.08
            Konqueror          301           1.00
        
            Create a new index and reindex the dataframe. By default
            values in the new index that do not have corresponding
            records in the dataframe are assigned ``NaN``.
        
            >>> new_index = ['Safari', 'Iceweasel', 'Comodo Dragon', 'IE10',
            ...              'Chrome']
            >>> df.reindex(new_index)
                           http_status  response_time
            Safari               404.0           0.07
            Iceweasel              NaN            NaN
            Comodo Dragon          NaN            NaN
            IE10                 404.0           0.08
            Chrome               200.0           0.02
        
            We can fill in the missing values by passing a value to
            the keyword ``fill_value``. Because the index is not monotonically
            increasing or decreasing, we cannot use arguments to the keyword
            ``method`` to fill the ``NaN`` values.
        
            >>> df.reindex(new_index, fill_value=0)
                           http_status  response_time
            Safari                 404           0.07
            Iceweasel                0           0.00
            Comodo Dragon            0           0.00
            IE10                   404           0.08
            Chrome                 200           0.02
        
            >>> df.reindex(new_index, fill_value='missing')
                          http_status response_time
            Safari                404          0.07
            Iceweasel         missing       missing
            Comodo Dragon     missing       missing
            IE10                  404          0.08
            Chrome                200          0.02
        
            We can also reindex the columns.
        
            >>> df.reindex(columns=['http_status', 'user_agent'])
                       http_status  user_agent
            Firefox            200         NaN
            Chrome             200         NaN
            Safari             404         NaN
            IE10               404         NaN
            Konqueror          301         NaN
        
            Or we can use "axis-style" keyword arguments
        
            >>> df.reindex(['http_status', 'user_agent'], axis="columns")
                       http_status  user_agent
            Firefox            200         NaN
            Chrome             200         NaN
            Safari             404         NaN
            IE10               404         NaN
            Konqueror          301         NaN
        
            To further illustrate the filling functionality in
            ``reindex``, we will create a dataframe with a
            monotonically increasing index (for example, a sequence
            of dates).
        
            >>> date_index = pd.date_range('1/1/2010', periods=6, freq='D')
            >>> df2 = pd.DataFrame({"prices": [100, 101, np.nan, 100, 89, 88]},
            ...                    index=date_index)
            >>> df2
                        prices
            2010-01-01   100.0
            2010-01-02   101.0
            2010-01-03     NaN
            2010-01-04   100.0
            2010-01-05    89.0
            2010-01-06    88.0
        
            Suppose we decide to expand the dataframe to cover a wider
            date range.
        
            >>> date_index2 = pd.date_range('12/29/2009', periods=10, freq='D')
            >>> df2.reindex(date_index2)
                        prices
            2009-12-29     NaN
            2009-12-30     NaN
            2009-12-31     NaN
            2010-01-01   100.0
            2010-01-02   101.0
            2010-01-03     NaN
            2010-01-04   100.0
            2010-01-05    89.0
            2010-01-06    88.0
            2010-01-07     NaN
        
            The index entries that did not have a value in the original data frame
            (for example, '2009-12-29') are by default filled with ``NaN``.
            If desired, we can fill in the missing values using one of several
            options.
        
            For example, to back-propagate the last valid value to fill the ``NaN``
            values, pass ``bfill`` as an argument to the ``method`` keyword.
        
            >>> df2.reindex(date_index2, method='bfill')
                        prices
            2009-12-29   100.0
            2009-12-30   100.0
            2009-12-31   100.0
            2010-01-01   100.0
            2010-01-02   101.0
            2010-01-03     NaN
            2010-01-04   100.0
            2010-01-05    89.0
            2010-01-06    88.0
            2010-01-07     NaN
        
            Please note that the ``NaN`` value present in the original dataframe
            (at index value 2010-01-03) will not be filled by any of the
            value propagation schemes. This is because filling while reindexing
            does not look at dataframe values, but only compares the original and
            desired indexes. If you do want to fill in the ``NaN`` values present
            in the original dataframe, use the ``fillna()`` method.
        
            See the :obj:`user guide <basics.reindexing>` for more.
        """

        print('-'*80)
        print('DataFrame: df')
        df = self.df1.add(pd.Series([1, 3, 5], index=pd.date_range('2010.1.1', periods=3)), axis=0)
        print(df)

        # labels-axis
        # -- axis=0
        print('='*80)
        print("newlabels = pd.date_range('2010.1.2', periods=2)")
        newlabels = pd.date_range('2010.1.2', periods=2)
        print("df.reindex(newlabels)")
        print(df.reindex(newlabels))
        print("newlabels = pd.date_range('2010.1.2', periods=3)")
        newlabels = pd.date_range('2010.1.2', periods=3)
        print("df.reindex(newlabels)")
        print(df.reindex(newlabels))

        print("df.reindex(labels=pd.date_range('2010.1.2', periods=3), axis=0, fill_value=100)")
        print(df.reindex(labels=pd.date_range('2010.1.2', periods=3), axis=0, fill_value=100))

        print("df.reindex(labels=pd.date_range('2010.1.2', periods=3), axis=0, method='pad')")
        print(df.reindex(labels=pd.date_range('2010.1.2', periods=3), axis=0, method='pad'))

        print("df.reindex(labels=pd.date_range('2010.1.2', periods=3), axis=0, method='nearest')")
        print(df.reindex(labels=pd.date_range('2010.1.2', periods=3), axis=0, method='nearest'))

        print("df.reindex(labels=['label-1', 'label-2'], axis=0, fill_value=0)")
        print(df.reindex(labels=['label-1', 'label-2'], axis=0, fill_value=0))

        # -- axis=1
        print('-'*80)
        print("df.reindex(labels=['A', 'C'], axis=1, fill_value=0)")
        print(df.reindex(labels=['A', 'C'], axis=1, fill_value=0))

        print("df.reindex(labels=['A', 'D'], axis=1, fill_value=100)")
        print(df.reindex(labels=['A', 'D'], axis=1, fill_value=100))

        # index-columns
        print('='*80)
        print("df.reindex(index=pd.date_range('2010.1.2', periods=3), columns=['A', 'C'], method=None)")
        newindex = pd.date_range('2010.1.2', periods=3)
        print(df.reindex(index=newindex, columns=['A', 'C'], method=None))

        print("df.reindex(index=pd.date_range('2010.1.2', periods=3), columns=['A', 'C', 'D'], method='bfill')")
        newindex = pd.date_range('2010.1.2', periods=3)
        print(df.reindex(index=newindex, columns=['A', 'C', 'D'], method='bfill'))

        print("df.reindex(index=pd.date_range('2010.1.2', periods=3), columns=['A', 'C', 'D'], method='ffill')")
        newindex = pd.date_range('2010.1.2', periods=3)
        print(df.reindex(index=newindex, columns=['A', 'C', 'D'], method='ffill'))

        # -- method = 'nearest' error
        try:
            print("df.reindex(index=pd.date_range('2010.1.1', periods=3), columns=['A', 'C', 'D'], method='nearest')")
            newindex = pd.date_range('2010.1.2', periods=3)
            print(df.reindex(index=newindex, columns=['A', 'C', 'D'], method='nearest'))
        except TypeError as e:
            print('TypeError: ', e)

        # -- reindex_like
        df1 = pd.DataFrame({'a': [1, 2, 3], 'b': [3, 5, 9]}, index=[1, 2, 3])
        df2 = pd.DataFrame({'a': [1, 2, 3], 'c': ['Beijing', 'Shanghai', 'Guangzhou']})
        print(df1)
        print(df2)
        print(df1.reindex_like(df2))

    def index_stack(self):
        """
        stack(self, level=-1, dropna=True)

            Stack the prescribed level(s) from columns to index.

            Return a reshaped DataFrame or Series having a multi-level
            index with one or more new inner-most levels compared to the current
            DataFrame. The new inner-most levels are created by pivoting the
            columns of the current dataframe:

              - if the columns have a single level, the output is a Series;
              - if the columns have multiple levels, the new index
                level(s) is (are) taken from the prescribed level(s) and
                the output is a DataFrame.

            Parameters
            ----------
            level : int, str, list, default -1
                Level(s) to stack from the column axis onto the index
                axis, defined as one index or label, or a list of indices
                or labels.
            dropna : bool, default True
                Whether to drop rows in the resulting Frame/Series with
                missing values. Stacking a column level onto the index
                axis can create combinations of index and column values
                that are missing from the original dataframe. See Examples
                section.

            Returns
            -------
            DataFrame or Series
                Stacked dataframe or series.

            See Also
            --------
            DataFrame.unstack : Unstack prescribed level(s) from index axis
                 onto column axis.
            DataFrame.pivot : Reshape dataframe from long format to wide
                 format.
            DataFrame.pivot_table : Create a spreadsheet-style pivot table
                 as a DataFrame.

            Notes
            -----
            The function is named by analogy with a collection of books
            being reorganized from being side by side on a horizontal
            position (the columns of the dataframe) to being stacked
            vertically on top of each other (in the index of the
            dataframe).

            Examples
            --------
            **Single level columns**

            >>> df_single_level_cols = pd.DataFrame([[0, 1], [2, 3]],
            ...                                     index=['cat', 'dog'],
            ...                                     columns=['weight', 'height'])

            Stacking a dataframe with a single level column axis returns a Series:

            >>> df_single_level_cols
                 weight height
            cat       0      1
            dog       2      3
            >>> df_single_level_cols.stack()
            cat  weight    0
                 height    1
            dog  weight    2
                 height    3
            dtype: int64

            **Multi level columns: simple case**

            >>> multicol1 = pd.MultiIndex.from_tuples([('weight', 'kg'),
            ...                                        ('weight', 'pounds')])
            >>> df_multi_level_cols1 = pd.DataFrame([[1, 2], [2, 4]],
            ...                                     index=['cat', 'dog'],
            ...                                     columns=multicol1)

            Stacking a dataframe with a multi-level column axis:

            >>> df_multi_level_cols1
                 weight
                     kg    pounds
            cat       1        2
            dog       2        4
            >>> df_multi_level_cols1.stack()
                        weight
            cat kg           1
                pounds       2
            dog kg           2
                pounds       4

            **Missing values**

            >>> multicol2 = pd.MultiIndex.from_tuples([('weight', 'kg'),
            ...                                        ('height', 'm')])
            >>> df_multi_level_cols2 = pd.DataFrame([[1.0, 2.0], [3.0, 4.0]],
            ...                                     index=['cat', 'dog'],
            ...                                     columns=multicol2)

            It is common to have missing values when stacking a dataframe
            with multi-level columns, as the stacked dataframe typically
            has more values than the original dataframe. Missing values
            are filled with NaNs:

            >>> df_multi_level_cols2
                weight height
                    kg      m
            cat    1.0    2.0
            dog    3.0    4.0
            >>> df_multi_level_cols2.stack()
                    height  weight
            cat kg     NaN     1.0
                m      2.0     NaN
            dog kg     NaN     3.0
                m      4.0     NaN

            **Prescribing the level(s) to be stacked**

            The first parameter controls which level or levels are stacked:

            >>> df_multi_level_cols2.stack(0)
                         kg    m
            cat height  NaN  2.0
                weight  1.0  NaN
            dog height  NaN  4.0
                weight  3.0  NaN
            >>> df_multi_level_cols2.stack([0, 1])
            cat  height  m     2.0
                 weight  kg    1.0
            dog  height  m     4.0
                 weight  kg    3.0
            dtype: float64

            **Dropping missing values**

            >>> df_multi_level_cols3 = pd.DataFrame([[None, 1.0], [2.0, 3.0]],
            ...                                     columns=multicol2)

            Note that rows where all values are missing are dropped by
            default but this behaviour can be controlled via the dropna
            keyword parameter:

            >>> df_multi_level_cols3
                weight height
                    kg      m
            cat    NaN    1.0
            dog    2.0    3.0
            >>> df_multi_level_cols3.stack(dropna=False)
                    height  weight
            cat kg     NaN     NaN
                m      1.0     NaN
            dog kg     NaN     2.0
                m      3.0     NaN
            >>> df_multi_level_cols3.stack(dropna=True)
                    height  weight
            cat m      1.0     NaN
            dog kg     NaN     2.0
                m      3.0     NaN
        """

        # unstack
        """
        unstack(self, level=-1, fill_value=None)
            Pivot a level of the (necessarily hierarchical) index labels.
        
            Returns a DataFrame having a new level of column labels whose inner-most level
            consists of the pivoted index labels.
        
            If the index is not a MultiIndex, the output will be a Series
            (the analogue of stack when the columns are not a MultiIndex).
        
            Parameters
            ----------
            level : int, str, or list of these, default -1 (last level)
                Level(s) of index to unstack, can pass level name.
            fill_value : int, str or dict
                Replace NaN with this value if the unstack produces missing values.
        
            Returns
            -------
            Series or DataFrame
        
            See Also
            --------
            DataFrame.pivot : Pivot a table based on column values.
            DataFrame.stack : Pivot a level of the column labels (inverse operation
                from `unstack`).
        
            Examples
            --------
            >>> index = pd.MultiIndex.from_tuples([('one', 'a'), ('one', 'b'),
            ...                                    ('two', 'a'), ('two', 'b')])
            >>> s = pd.Series(np.arange(1.0, 5.0), index=index)
            >>> s
            one  a   1.0
                 b   2.0
            two  a   3.0
                 b   4.0
            dtype: float64
        
            >>> s.unstack(level=-1)
                 a   b
            one  1.0  2.0
            two  3.0  4.0
        
            >>> s.unstack(level=0)
               one  two
            a  1.0   3.0
            b  2.0   4.0
        
            >>> df = s.unstack(level=0)
            >>> df.unstack()
            one  a  1.0
                 b  2.0
            two  a  3.0
                 b  4.0
            dtype: float64
        """

        print('='*80)
        # print('DataFrame: df')
        df1 = self.df3
        df2 = pd.DataFrame(data=np.random.randint(0,9, (4, 4)),
                           index=pd.MultiIndex.from_product([['class-1', 'class-2'], ['year_1', 'year_2']]),
                           columns=pd.MultiIndex.from_product([['math', 'science'], ['time_1', 'time_2']])
                           )
        df2.index.names = ['class', 'year']
        df2.columns.names = ['course', 'time']
        # with pd.option_context('display.max_rows', 5, 'display.max_columns', 5):
        # print("df1")
        # print(df1)
        print("df2")
        print(df2)

        print('='*80)
        print("列标签最后层次转为行标签最后层次")
        print('- '*40)
        print('df2.stack()')
        print(df2.stack())

        print('-'*80)
        print("行标签最后层次转为列标签最后层次")
        print('- '*40)
        print('df2.unstack()')
        print(df2.unstack())

        print('='*80)
        print("列标签第0层次转为行标签最后层次")
        print('- '*40)
        print('df2.stack(level=0)')
        print(df2.stack(level=0))

        print('-'*80)
        print("行标签第0层次转为列标签最后层次")
        print('- '*40)
        print('df2.unstack(level=0)')
        print(df2.unstack(level=0))

        print('='*80)
        print("stack/unstack可逆操作")
        print('- '*40)
        print('df2 == df2.stack().unstack()')
        print(df2.stack().unstack())

        print('-'*80)
        print("stack.stack转为Series")
        print('- '*40)
        print('df2.stack().stack()')
        print(df2.stack().stack())
        #
        print('-'*80)
        print("stack.stack.unstack由Series再转DataFrame")
        print('- '*40)
        print('df2.stack().stack().unstack()')
        print(df2.stack().stack().unstack())

        print('-'*80)
        print("Series只有单层索引时，不能使用unstack")
        print('- '*40)
        print(pd.Series(range(3), index=list('abc')))
        print("sr.unstack()")
        print("AttributeError: 'Index' object has no attribute 'remove_unused_levels'")

    def index_swap_level(self):
        """
        swaplevel(self, i=-2, j=-1, axis=0) -> 'DataFrame'
            Swap levels i and j in a MultiIndex on a particular axis.

            Parameters
            ----------
            i, j : int or str
                Levels of the indices to be swapped. Can pass level name as string.
            axis : {0 or 'index', 1 or 'columns'}, default 0
                The axis to swap levels on. 0 or 'index' for row-wise, 1 or
                'columns' for column-wise.

            Returns
            -------
            DataFrame
        """
        self.index_multi_index(True)
        df = self.df_mindex2.loc[1:2]
        df = df.swaplevel(i=0, j=1).sort_index().loc['t1':'t2'].swaplevel().sort_index()
        df.index.names = ['grade', 'syntax']
        print(" >>> df")
        print(df)

        print("交换索引层：>>> df.swaplevel(i=0, j=1)")
        print(df.swaplevel(i=0, j=1))

        print("重排序索引")
        print("df.swaplevel(i=0, j=1).sort_index()")
        print(df.swaplevel(i=0, j=1).sort_index())

        print("交换列索引层：>>> df.swaplevel(i=0, j=1, axis=1)")
        df = df.unstack()
        print(df)
        print(df.swaplevel(i=0, j=1, axis=1))
        df = df.stack()

        print("轴索引交换")
        print(" >>> df")
        print(df)
        print("df.swapaxes(axis1=0, axis2=1)")
        print(df.swapaxes(axis1=0, axis2=1))

    def index_rename(self):
        """
        rename(mapper=None, index=None, columns=None, axis=None, copy=True, inplace=False, level=None)

            method of pandas.core.frame.DataFrame instance

            Alter axes labels.

            Function / dict values must be unique (1-to-1). Labels not contained in
            a dict / Series will be left as-is. Extra labels listed don't throw an
            error.

            See the :obj:`user guide <basics.rename>` for more.

            Parameters
            ----------
            mapper, index, columns : dict-like or function, optional
                dict-like or functions transformations to apply to
                that axis' values. Use either ``mapper`` and ``axis`` to
                specify the axis to target with ``mapper``, or ``index`` and
                ``columns``.
            axis : int or str, optional
                Axis to target with ``mapper``. Can be either the axis name
                ('index', 'columns') or number (0, 1). The default is 'index'.
            copy : boolean, default True
                Also copy underlying data
            inplace : boolean, default False
                Whether to return a new DataFrame. If True then value of copy is
                ignored.
            level : int or level name, default None
                In case of a MultiIndex, only rename labels in the specified
                level.

            Returns
            -------
            renamed : DataFrame

            See Also
            --------
            pandas.DataFrame.rename_axis

            Examples
            --------

            支持两种调用风格
            ``DataFrame.rename`` supports two calling conventions

            index、columns分别设置标签替换映射
            * ``(index=index_mapper, columns=columns_mapper, ...)``
            设置映射、再指定轴axis为'index'或'columns'
            * ``(mapper, axis={'index', 'columns'}, ...)``

            强烈推荐使用关键字设置
            We *highly* recommend using keyword arguments to clarify your intent.

            >>> df = pd.DataFrame({"A": [1, 2, 3], "B": [4, 5, 6]})
            >>> df.rename(index=str, columns={"A": "a", "B": "c"})
               a  c
            0  1  4
            1  2  5
            2  3  6

            >>> df.rename(index=str, columns={"A": "a", "C": "c"})
               a  B
            0  1  4
            1  2  5
            2  3  6

            Using axis-style parameters

            >>> df.rename(str.lower, axis='columns')
               a  b
            0  1  4
            1  2  5
            2  3  6

            >>> df.rename({1: 2, 2: 4}, axis='index')
               A  B
            0  1  4
            2  2  5
            4  3  6
        """

    def index_set_axis(self):
        """
        set_axis(labels, axis: 'Axis' = 0, inplace: 'bool' = False)

            method of pandas.core.frame.DataFrame instance

            Assign desired index to given axis.

            Indexes for column or row labels can be changed by assigning
            a list-like or Index.

            Parameters
            ----------
            labels : list-like, Index
                The values for the new index.

            axis : {0 or 'index', 1 or 'columns'}, default 0
                The axis to update. The value 0 identifies the rows, and 1 identifies the columns.

            inplace : bool, default False
                Whether to return a new DataFrame instance.

            Returns
            -------
            renamed : DataFrame or None
                An object of type DataFrame or None if ``inplace=True``.

            See Also
            --------
            DataFrame.rename_axis : Alter the name of the index or columns.

                    Examples
                    --------
                    >>> df = pd.DataFrame({"A": [1, 2, 3], "B": [4, 5, 6]})

                    Change the row labels.

                    >>> df.set_axis(['a', 'b', 'c'], axis='index')
                       A  B
                    a  1  4
                    b  2  5
                    c  3  6

                    Change the column labels.

                    >>> df.set_axis(['I', 'II'], axis='columns')
                       I  II
                    0  1   4
                    1  2   5
                    2  3   6

                    Now, update the labels inplace.

                    >>> df.set_axis(['i', 'ii'], axis='columns', inplace=True)
                    >>> df
                       i  ii
                    0  1   4
                    1  2   5
                    2  3   6
        """

    def test_exec(self):
        a = 2
        x = 1
        my_str = "x = a*100"
        locvars = locals()
        exec(my_str)
        print(my_str, '=', locvars['x'])
